Commit f3241cd4 authored by Robert Schmidt's avatar Robert Schmidt

Merge branch 'integration_2022_wk19' into 'develop'

integration_2022_wk19

See merge request oai/openairinterface5g!1553

MR !1456 TBSLBRM for DL computed ad MAC
MR !1532 CSI-IM harmonization
MR !1538 April 2022: some last CI fixes
MR !1540 Documentation Update
MR !1541 [CI] amarisoft UE server IP address update
MR !1546 ci-harmonization-and-cleanup
MR !1547 CI architecture description and RFsim updates
MR !1548 Fix Ubuntu 22 build_helper
parents d11350c0 bfb7713f
......@@ -59,7 +59,7 @@ if [ $# -eq 0 ]
then
echo " ---- Checking the whole repository ----"
echo ""
NB_FILES_TO_FORMAT=`astyle --dry-run --options=ci-scripts/astyle-options.txt --recursive *.c *.h | grep -c Formatted || true`
NB_FILES_TO_FORMAT=`astyle --dry-run --options=ci-scripts/astyle-options.txt --recursive --exclude=ci-scripts --exclude=cmake_targets *.c *.h | grep -c Formatted || true`
echo "Nb Files that do NOT follow OAI rules: $NB_FILES_TO_FORMAT"
echo $NB_FILES_TO_FORMAT > ./oai_rules_result.txt
......@@ -136,7 +136,7 @@ fi
# Merge request scenario
MERGE_COMMMIT=`git log -n1 --pretty=format:%H`
TARGET_INIT_COMMIT=`cat .git/refs/remotes/origin/$TARGET_BRANCH`
TARGET_INIT_COMMIT=`git log -n1 --pretty=format:%H origin/$TARGET_BRANCH`
echo " ---- Checking the modified files by the merge request ----"
echo ""
......
......@@ -55,7 +55,7 @@ amarisoft_ue_1:
Duration : 60
Ping : /tmp/test_ue1.log
UELog : /tmp/ue1.log
HostIPAddress : 192.168.18.89
HostIPAddress : 172.21.16.144
HostUsername : root
HostPassword : toor
HostSourceCodePath : /tmp
......
......@@ -37,6 +37,7 @@ import logging
import os
from pathlib import Path
import time
from multiprocessing import Process, Lock, SimpleQueue
#-----------------------------------------------------------
# OAI Testing modules
......@@ -237,3 +238,205 @@ class StaticCodeAnalysis():
return 0
def LicenceAndFormattingCheck(self, HTML):
if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
HELP.GenericHelp(CONST.Version)
sys.exit('Insufficient Parameter')
lIpAddr = self.eNBIPAddress
lUserName = self.eNBUserName
lPassWord = self.eNBPassword
lSourcePath = self.eNBSourceCodePath
if lIpAddr == '' or lUserName == '' or lPassWord == '' or lSourcePath == '':
HELP.GenericHelp(CONST.Version)
sys.exit('Insufficient Parameter')
logging.debug('Building on server: ' + lIpAddr)
mySSH = SSH.SSHConnection()
mySSH.open(lIpAddr, lUserName, lPassWord)
self.testCase_id = HTML.testCase_id
# on RedHat/CentOS .git extension is mandatory
result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
if result is not None:
full_ran_repo_name = self.ranRepository.replace('git/', 'git')
else:
full_ran_repo_name = self.ranRepository + '.git'
mySSH.command('mkdir -p ' + lSourcePath, '\$', 5)
mySSH.command('cd ' + lSourcePath, '\$', 5)
mySSH.command('if [ ! -e .git ]; then stdbuf -o0 git clone ' + full_ran_repo_name + ' .; else stdbuf -o0 git fetch --prune; fi', '\$', 600)
# Raphael: here add a check if git clone or git fetch went smoothly
mySSH.command('git config user.email "jenkins@openairinterface.org"', '\$', 5)
mySSH.command('git config user.name "OAI Jenkins"', '\$', 5)
mySSH.command('echo ' + lPassWord + ' | sudo -S git clean -x -d -ff', '\$', 30)
mySSH.command('mkdir -p cmake_targets/log', '\$', 5)
# if the commit ID is provided use it to point to it
if self.ranCommitID != '':
mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 30)
# if the branch is not develop, then it is a merge request and we need to do
# the potential merge. Note that merge conflicts should already been checked earlier
argToPass = ''
if (self.ranAllowMerge):
argToPass = '--build-arg MERGE_REQUEST=true --build-arg SRC_BRANCH=' + self.ranBranch
if self.ranTargetBranch == '':
if (self.ranBranch != 'develop') and (self.ranBranch != 'origin/develop'):
mySSH.command('git merge --ff origin/develop -m "Temporary merge for CI"', '\$', 5)
argToPass += ' --build-arg TARGET_BRANCH=develop '
else:
logging.debug('Merging with the target branch: ' + self.ranTargetBranch)
mySSH.command('git merge --ff origin/' + self.ranTargetBranch + ' -m "Temporary merge for CI"', '\$', 5)
argToPass += ' --build-arg TARGET_BRANCH=' + self.ranTargetBranch + ' '
mySSH.command('docker image rm oai-formatting-check:latest || true', '\$', 60)
mySSH.command('docker build --target oai-formatting-check --tag oai-formatting-check:latest ' + argToPass + '--file ci-scripts/docker/Dockerfile.formatting.bionic . > cmake_targets/log/oai-formatting-check.txt 2>&1', '\$', 600)
mySSH.command('docker image rm oai-formatting-check:latest || true', '\$', 60)
mySSH.command('docker image prune --force', '\$', 60)
mySSH.command('docker volume prune --force', '\$', 60)
# Analyzing the logs
mySSH.command('cd ' + lSourcePath + '/cmake_targets', '\$', 5)
mySSH.command('mkdir -p build_log_' + self.testCase_id, '\$', 5)
mySSH.command('mv log/* ' + 'build_log_' + self.testCase_id, '\$', 5)
mySSH.close()
mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/build_log_' + self.testCase_id + '/*', '.')
finalStatus = 0
if (os.path.isfile('./oai-formatting-check.txt')):
analyzed = False
nbFilesNotFormatted = 0
listFiles = False
listFilesNotFormatted = []
circularHeaderDependency = False
circularHeaderDependencyFiles = []
gnuGplLicence = False
gnuGplLicenceFiles = []
suspectLicence = False
suspectLicenceFiles = []
with open('./oai-formatting-check.txt', 'r') as logfile:
for line in logfile:
ret = re.search('./ci-scripts/checkCodingFormattingRules.sh', str(line))
if ret is not None:
analyzed = True
if analyzed:
ret = re.search('Nb Files that do NOT follow OAI rules: (?P<nb_errors>[0-9\.]+)', str(line))
if ret is not None:
nbFilesNotFormatted = int(ret.group('nb_errors'))
if re.search('=== Files not properly formatted ===', str(line)) is not None:
listFiles = True
if listFiles:
if re.search('Removing intermediate container', str(line)) is not None:
listFiles = False
elif re.search('Running in|Files not properly formatted', str(line)) is not None:
pass
else:
listFilesNotFormatted.append(str(line).strip())
if re.search('=== Files with incorrect define protection ===', str(line)) is not None:
circularHeaderDependency = True
if circularHeaderDependency:
if re.search('Removing intermediate container', str(line)) is not None:
circularHeaderDependency = False
elif re.search('Running in|Files with incorrect define protection', str(line)) is not None:
pass
else:
circularHeaderDependencyFiles.append(str(line).strip())
if re.search('=== Files with a GNU GPL licence Banner ===', str(line)) is not None:
gnuGplLicence = True
if gnuGplLicence:
if re.search('Removing intermediate container', str(line)) is not None:
gnuGplLicence = False
elif re.search('Running in|Files with a GNU GPL licence Banner', str(line)) is not None:
pass
else:
gnuGplLicenceFiles.append(str(line).strip())
if re.search('=== Files with a suspect Banner ===', str(line)) is not None:
suspectLicence = True
if suspectLicence:
if re.search('Removing intermediate container', str(line)) is not None:
suspectLicence = False
elif re.search('Running in|Files with a suspect Banner', str(line)) is not None:
pass
else:
suspectLicenceFiles.append(str(line).strip())
logfile.close()
if analyzed:
logging.debug('files not formatted properly: ' + str(nbFilesNotFormatted))
if nbFilesNotFormatted == 0:
HTML.CreateHtmlTestRow('File(s) Format', 'OK', CONST.ALL_PROCESSES_OK)
else:
html_queue = SimpleQueue()
html_cell = '<pre style="background-color:white">\n'
html_cell += 'Number of files not following OAI Rules: ' + str(nbFilesNotFormatted) + '\n'
for nFile in listFilesNotFormatted:
html_cell += str(nFile).strip() + '\n'
html_cell += '</pre>'
html_queue.put(html_cell)
HTML.CreateHtmlTestRowQueue('File(s) Format', 'KO', 1, html_queue)
del(html_cell)
del(html_queue)
logging.debug('header files not respecting the circular dependency protection: ' + str(len(circularHeaderDependencyFiles)))
if len(circularHeaderDependencyFiles) == 0:
HTML.CreateHtmlTestRow('Header Circular Dependency', 'OK', CONST.ALL_PROCESSES_OK)
else:
html_queue = SimpleQueue()
html_cell = '<pre style="background-color:white">\n'
html_cell += 'Number of files not respecting: ' + str(len(circularHeaderDependencyFiles)) + '\n'
for nFile in circularHeaderDependencyFiles:
html_cell += str(nFile).strip() + '\n'
html_cell += '</pre>'
html_queue.put(html_cell)
HTML.CreateHtmlTestRowQueue('Header Circular Dependency', 'KO', 1, html_queue)
del(html_cell)
del(html_queue)
finalStatus = -1
logging.debug('files with a GNU GPL license: ' + str(len(gnuGplLicenceFiles)))
if len(gnuGplLicenceFiles) == 0:
HTML.CreateHtmlTestRow('Files w/ GNU GPL License', 'OK', CONST.ALL_PROCESSES_OK)
else:
html_queue = SimpleQueue()
html_cell = '<pre style="background-color:white">\n'
html_cell += 'Number of files not respecting: ' + str(len(gnuGplLicenceFiles)) + '\n'
for nFile in gnuGplLicenceFiles:
html_cell += str(nFile).strip() + '\n'
html_cell += '</pre>'
html_queue.put(html_cell)
HTML.CreateHtmlTestRowQueue('Files w/ GNU GPL License', 'KO', 1, html_queue)
del(html_cell)
del(html_queue)
finalStatus = -1
logging.debug('files with a suspect license: ' + str(len(suspectLicenceFiles)))
if len(suspectLicenceFiles) == 0:
HTML.CreateHtmlTestRow('Files with suspect license', 'OK', CONST.ALL_PROCESSES_OK)
else:
html_queue = SimpleQueue()
html_cell = '<pre style="background-color:white">\n'
html_cell += 'Number of files not respecting: ' + str(len(suspectLicenceFiles)) + '\n'
for nFile in suspectLicenceFiles:
html_cell += str(nFile).strip() + '\n'
html_cell += '</pre>'
html_queue.put(html_cell)
HTML.CreateHtmlTestRowQueue('Files with suspect license', 'KO', 1, html_queue)
del(html_cell)
del(html_queue)
finalStatus = -1
else:
finalStatus = -1
HTML.htmleNBFailureMsg = 'Could not fully analyze oai-formatting-check.txt file'
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE)
else:
finalStatus = -1
HTML.htmleNBFailureMsg = 'Could not access oai-formatting-check.txt file'
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE)
return finalStatus
FROM ubuntu:bionic AS oai-formatting-check
ARG MERGE_REQUEST
ARG SRC_BRANCH
ARG TARGET_BRANCH
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \
DEBIAN_FRONTEND=noninteractive apt-get install --yes \
astyle \
gawk \
git
WORKDIR /oai-ran
COPY . .
RUN /bin/bash -c "if [[ -v MERGE_REQUEST ]]; then echo 'Source Branch = $SRC_BRANCH'; echo 'Target Branch = $TARGET_BRANCH'; else echo 'Push to develop'; fi"
RUN /bin/bash -c "if [[ -v MERGE_REQUEST ]]; then ./ci-scripts/checkCodingFormattingRules.sh --src-branch $SRC_BRANCH --target-branch $TARGET_BRANCH; else ./ci-scripts/checkCodingFormattingRules.sh; fi"
RUN echo "=== Files not properly formatted ===" && \
/bin/bash -c "if [[ -f oai_rules_result_list.txt ]]; then cat oai_rules_result_list.txt; fi"
RUN echo "=== Files with incorrect define protection ===" && \
/bin/bash -c "if [[ -f header-files-w-incorrect-define.txt ]]; then cat header-files-w-incorrect-define.txt; fi"
RUN echo "=== Files with a GNU GPL licence Banner ===" && \
/bin/bash -c "if [[ -f files-w-gnu-gpl-license-banner.txt ]]; then cat files-w-gnu-gpl-license-banner.txt; fi"
RUN echo "=== Files with a suspect Banner ===" && \
/bin/bash -c "if [[ -f files-w-suspect-banner.txt ]]; then cat files-w-suspect-banner.txt; fi"
......@@ -255,16 +255,19 @@ class EPCManagement():
mySSH.command('mkdir -p ' + self.SourceCodePath + '/scripts', '\$', 5)
mySSH.command('cd /opt/oai-cn5g-fed-v1.3/docker-compose', '\$', 5)
mySSH.command('python3 ./core-network.py '+self.cfgDeploy, '\$', 60)
time.sleep(2)
mySSH.command('docker-compose -p 5gcn ps -a', '\$', 60)
if re.search('start-mini-as-ue', self.cfgDeploy):
dFile = 'docker-compose-mini-nrf-asue.yaml'
else:
dFile = 'docker-compose-mini-nrf.yaml'
mySSH.command('docker-compose -p 5gcn -f ' + dFile + ' ps -a', '\$', 60)
if mySSH.getBefore().count('Up (healthy)') != 6:
logging.error('Not all container healthy')
else:
logging.debug('OK')
mySSH.command('docker-compose config | grep --colour=never image', '\$', 10)
logging.debug('OK --> all containers are healthy')
mySSH.command('docker-compose -p 5gcn -f ' + dFile + ' config | grep --colour=never image', '\$', 10)
listOfImages = mySSH.getBefore()
for imageLine in listOfImages.split('\\r\\n'):
res1 = re.search('image: (?P<name>[a-zA-Z0-9\-]+):(?P<tag>[a-zA-Z0-9\-]+)', str(imageLine))
res1 = re.search('image: (?P<name>[a-zA-Z0-9\-/]+):(?P<tag>[a-zA-Z0-9\-]+)', str(imageLine))
res2 = re.search('mysql', str(imageLine))
if res1 is not None and res2 is None:
html_cell += res1.group('name') + ':' + res1.group('tag') + ' '
......@@ -536,7 +539,7 @@ class EPCManagement():
mySSH.command('python3 ./core-network.py '+self.cfgUnDeploy, '\$', 60)
mySSH.command('docker volume prune --force || true', '\$', 60)
time.sleep(2)
mySSH.command('tshark -r /tmp/oai-cn5g.pcap | egrep --colour=never "Tracking area update" ','\$', 30)
mySSH.command('tshark -r /tmp/oai-cn5g-v1.3.pcap | egrep --colour=never "Tracking area update" ','\$', 30)
result = re.search('Tracking area update request', mySSH.getBefore())
if result is not None:
message = 'UE requested ' + str(mySSH.getBefore().count('Tracking area update request')) + 'Tracking area update request(s)'
......@@ -830,8 +833,8 @@ class EPCManagement():
mySSH.command('zip mme.log.zip mme_check_run.*', '\$', 60)
elif re.match('OAICN5G', self.Type, re.IGNORECASE):
mySSH.command('cd ' + self.SourceCodePath + '/logs','\$', 5)
mySSH.command('cp -f /tmp/oai-cn5g.pcap .','\$', 30)
mySSH.command('zip mme.log.zip oai-amf.log oai-nrf.log oai-cn5g.pcap','\$', 30)
mySSH.command('cp -f /tmp/oai-cn5g-v1.3.pcap .','\$', 30)
mySSH.command('zip mme.log.zip oai-amf.log oai-nrf.log oai-cn5g*.pcap','\$', 30)
mySSH.command('mv mme.log.zip ' + self.SourceCodePath + '/scripts','\$', 30)
elif re.match('OAI', self.Type, re.IGNORECASE) or re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
mySSH.command('zip mme.log.zip mme*.log', '\$', 60)
......
......@@ -799,7 +799,7 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
HTML.startTime=int(round(time.time() * 1000))
while CiTestObj.FailReportCnt < CiTestObj.repeatCounts[0] and RAN.prematureExit:
RAN.prematureExit=False
# At every iteratin of the retry loop, a separator will be added
# At every iteration of the retry loop, a separator will be added
# pass CiTestObj.FailReportCnt as parameter of HTML.CreateHtmlRetrySeparator
HTML.CreateHtmlRetrySeparator(CiTestObj.FailReportCnt)
for test_case_id in todo_tests:
......@@ -923,6 +923,10 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
CONTAINERS.UndeployObject(HTML, RAN)
elif action == 'Cppcheck_Analysis':
SCA.CppCheckAnalysis(HTML)
elif action == 'LicenceAndFormattingCheck':
ret = SCA.LicenceAndFormattingCheck(HTML)
if ret != 0:
RAN.prematureExit = True
elif action == 'Deploy_Run_PhySim':
PHYSIM.Deploy_PhySim(HTML, RAN)
elif action == 'DeployGenObject':
......
......@@ -47,3 +47,4 @@
- PingFromContainer
- IperfFromContainer
- StatsFromGenObject
- LicenceAndFormattingCheck
......@@ -142,8 +142,8 @@
<iperf_args>-u -b 125M -t 60 -i 1 -fm</iperf_args>
<direction>DL</direction>
<id>idefix</id>
<iperf_packetloss_threshold>1</iperf_packetloss_threshold>
<iperf_bitrate_threshold>95</iperf_bitrate_threshold>
<iperf_packetloss_threshold>25</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
<iperf_profile>single-ue</iperf_profile>
</testCase>
......
<!--
Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The OpenAirInterface Software Alliance licenses this file to You under
the OAI Public License, Version 1.1 (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.openairinterface.org/?page_id=698
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.
For more information about the OpenAirInterface (OAI) Software Alliance:
contact@openairinterface.org
-->
<testCaseList>
<htmlTabRef>formatting-tab</htmlTabRef>
<htmlTabName>License and Formatting Checks</htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<TestCaseRequestedList>
000002
</TestCaseRequestedList>
<TestCaseExclusionList></TestCaseExclusionList>
<testCase id="000002">
<class>LicenceAndFormattingCheck</class>
<desc>License and Formatting Checks</desc>
</testCase>
</testCaseList>
......@@ -34,8 +34,8 @@
050000
050001
000001
070003
070002
070000
070001
000001
050000
050001
......@@ -52,14 +52,12 @@
<UE_Trace>yes</UE_Trace>
</testCase>
<testCase id="010002">
<class>Terminate_UE</class>
<desc>Terminate Quectel</desc>
<id>idefix</id>
</testCase>
<testCase id="030000">
<class>Initialize_eNB</class>
<desc>Initialize eNB</desc>
......@@ -70,7 +68,6 @@
<eNB_Trace>yes</eNB_Trace>
</testCase>
<testCase id="040000">
<class>Initialize_eNB</class>
<desc>Initialize gNB</desc>
......@@ -92,7 +89,6 @@
<idle_sleep_time_in_sec>20</idle_sleep_time_in_sec>
</testCase>
<testCase id="050000">
<class>Ping</class>
<desc>Ping: 20pings in 20sec</desc>
......@@ -112,39 +108,17 @@
</testCase>
<testCase id="070000">
<class>Iperf</class>
<desc>iperf (DL/30Mbps/UDP)(30 sec)(single-ue profile)</desc>
<iperf_args>-u -b 30M -t 30</iperf_args>
<direction>DL</direction>
<id>idefix</id>
<iperf_packetloss_threshold>1</iperf_packetloss_threshold>
<iperf_bitrate_threshold>95</iperf_bitrate_threshold>
<iperf_profile>single-ue</iperf_profile>
</testCase>
<testCase id="070001">
<class>Iperf</class>
<desc>iperf (DL/90Mbps/UDP)(30 sec)(single-ue profile)</desc>
<iperf_args>-u -b 90M -t 30</iperf_args>
<direction>DL</direction>
<id>idefix</id>
<iperf_packetloss_threshold>1</iperf_packetloss_threshold>
<iperf_bitrate_threshold>95</iperf_bitrate_threshold>
<iperf_profile>single-ue</iperf_profile>
</testCase>
<testCase id="070002">
<class>Iperf</class>
<desc>iperf (DL/125Mbps/UDP)(60 sec)(single-ue profile)</desc>
<iperf_args>-u -b 125M -t 60</iperf_args>
<direction>DL</direction>
<id>idefix</id>
<iperf_packetloss_threshold>1</iperf_packetloss_threshold>
<iperf_bitrate_threshold>95</iperf_bitrate_threshold>
<iperf_packetloss_threshold>25</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
<iperf_profile>single-ue</iperf_profile>
</testCase>
<testCase id="070003">
<testCase id="070001">
<class>Iperf</class>
<desc>iperf (UL/8Mbps/UDP)(60 sec)(single-ue profile)</desc>
<iperf_args>-u -b 8M -t 60</iperf_args>
......@@ -155,7 +129,6 @@
<iperf_profile>single-ue</iperf_profile>
</testCase>
<testCase id="080000">
<class>Terminate_eNB</class>
<desc>Terminate eNB</desc>
......
......@@ -381,7 +381,7 @@ check_install_usrp_uhd_driver(){
$SUDO apt-get remove libuhd3.14.1 -y || true
$SUDO apt-get remove libuhd3.15.0 -y || true
local distribution=$(get_distribution_release)
if [[ "$distribution" == "ubuntu18.04" ]]; then
if [[ "$distribution" == "ubuntu18.04" || "$distribution" == "ubuntu20.04" || "$distribution" == "ubuntu22.04" ]]; then
$SUDO apt-get remove libuhd4.0.0 -y || true
$SUDO apt-get remove libuhd4.1.0 -y || true
fi
......@@ -410,13 +410,15 @@ check_install_usrp_uhd_driver(){
x=$((x + 1))
done
$SUDO apt-get update
$SUDO apt-get -y install python python-tk libboost-all-dev libusb-1.0-0-dev
if [[ "$distribution" == "ubuntu16.04" ]]; then
$SUDO apt-get -y install python-tk libboost-all-dev libusb-1.0-0-dev
case "$(get_distribution_release)" in
"ubuntu16.04")
$SUDO apt-get -y install libuhd-dev libuhd3.15.0 uhd-host
fi
if [[ "$distribution" == "ubuntu18.04" ]]; then
$SUDO apt-get -y install libuhd-dev libuhd4.1.0 uhd-host
fi
;;
"ubuntu18.04" | "ubuntu20.04" | "ubuntu22.04")
$SUDO apt-get -y install libuhd-dev libuhd4.2.0 uhd-host
;;
esac
elif [[ "$OS_BASEDISTRO" == "fedora" ]]; then
if [ $IS_CONTAINER -eq 0 ]
then
......
......@@ -193,6 +193,7 @@ Using the help option of the build script you can get the list of available opti
| --UE | maintained and tested in CI | build `lte-uesoftmodem` the LTE UE |
| --gNB | maintained and tested in CI | build `nr-softmodem` the 5G gNodeB |
| --nrUE | maintained and tested in CI | build `nr-uesoftmodem` the 5G UE |
| --arch-native | maintained | build with native architecture optimization |
| --usrp-recplay | deprecated | use the USRP configuration parameters to use the record player. |
| --build-lib | maintained | build optional shared library(ies), which can then be loaded at run time via command line option. Use the --help option to get the list of supported optional libraries. `all` can be used to build all available optional libraries. |
| --UE-conf-nvram | maintained | Specifies the path to the input file used by the conf2uedata utility. defaults to [openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf](../openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf) |
......
## Table of Contents ##
1. [Legacy 1 Bench](#legacy-1-bench)
2. [Legacy 2 Bench](#legacy-2-bench)
3. [Next Bench for DEV](#next-bench-for-dev)
4. [Next Bench for CI](#next-bench-for-ci)
5. [Indoor Live Network Bench](#indoor-live-network-bench)
6. [Outdoor Live Network Bench](#outdoor-live-network-bench)
1. [Machines](#machines)
2. [Networked devices](#networked-devices)
3. [Testbenches](#testbenches)
4. [Pipelines](#pipelines)
## Machines
## Legacy 1 Bench
**Purpose** : FDD Band 7 and Band 13, LTE-M
**Note** : Legacy1 and Legacy2 are duplicated so that they can run in parallel, thus avoiding a CI bottleneck
**Note** : Faraday Cages 1 and 2 are physically the same cage
![image info](./testbenches_doc_resources/legacy1.jpg)
## Legacy 2 Bench
**Purpose** : TDD Band 40, TM2 2xTX 2xRX
**Note** : CN can run in a container, could also run on Massive
![image info](./testbenches_doc_resources/legacy2.jpg)
## Next Bench for DEV
**Note** : Benetel CI can also run on this bench at night
![image info](./testbenches_doc_resources/next_dev.jpg)
## Next Bench for CI
**Note** : The current test running on Caracal could run on this bench with a N300/N300 setup
![image info](./testbenches_doc_resources/next_ci.jpg)
## Indoor Live Network Bench
![image info](./testbenches_doc_resources/indoor_live.jpg)
## Outdoor Live Network Bench
![image info](./testbenches_doc_resources/outdoor_live.jpg)
| Machine | IP address | Lockable Resource | Function | Connected devices |
| ------------- | --------------- | --------------------- | ------------------ | ----------------------------------------------------- |
| asterix | 172.21.16.127 | CI-Asterix-Usage | gNB | 173.21.19.14 |
| obelix | 172.21.16.128 | CI-Obelix-Usage | eNB, UE (5G) | 172.21.19.13, X300 (192.168.60.2), B200mini (30C51EB) |
| porcepix | 172.21.16.136 | CI-NSA-MiniBench | Executor, EPC, 5GC | -- |
| nrmodule2 | 172.21.16.139 | CI-NSA-MiniBench | Quectel | Quectel module |
| nepes | 172.21.16.137 | CI-NSA-MiniBench | gNB | B200mini (30C51D4) |
| caracal | 172.21.16.132 | CI-Caracal | gNB/phytest | N300 (192.168.10.2) |
| idefix | 172.21.16.135 | CI-NSA-MiniBench | Quectel | Quectel module |
| amariue | 172.21.16.144 | CI-Amarisoft-UE-Usage | TBD | Amarisoft UE simulator |
| bellatrix | 192.168.117.115 | CI-RAN-VM-Deployment | Executor | -- |
| nano | 192.168.12.62 | CI-Bench-1-Phones | EPC, adb | 2x COTS (adb) |
| hutch | 192.168.12.19 | CI-Bench-1-Phones | eNB (B7) | B200mini (30C5239) |
| starsky | 192.168.12.18 | CI-Bench-1-Phones | eNB (B40) | b200mini (30A3E3C) |
| carabe | 192.168.12.211 | CI-Bench-2-OAI-Phone | UE 4G (B) | B200mini (30AE8C9) |
Note: The available resources, and their current usage, is indicated here:
- [Lockable resources of jenkins-oai](https://jenkins-oai.eurecom.fr/lockable-resources/):
"New" Jenkins, i.e., with RAN-Container-Parent
- [Lockable resources of open5glab jenkins](https://open5glab.eurecom.fr:8083/jenkins/lockable-resources/):
"Old" Jenkins, i.e., with RAN-CI-Develop
## Networked devices
| Type | IP address (mgmt) |
| ------------- | ----------------- |
| USRP N310 | 172.21.19.14 |
| USRP N310 | 172.21.19.13 |
## Testbenches
### OTA Testbench
[Proper image to be followed up. TBD: add antennas/circulators]
Note: obelix and porcepix are both used in the OTA testbench and the 5G
NSA/Faraday Cage testbench!
![5G OTA Testbench](testbenches_doc_resources/5g-ota-bench.png)
[PDF version](testbenches_doc_resources/5g-ota-bench.pdf) | [LaTeX/TikZ version](testbenches_doc_resources/5g-ota-bench.tex) if you want to modify to reflect your setup
### 5G NSA/Faraday Cage Testbench
**Purpose**: Faraday cage 5G tests
Note: obelix and porcepix are both used in the OTA testbench and the 5G
NSA/Faraday Cage testbench!
![5G NSA/Faraday Cage Testbench](testbenches_doc_resources/5g-nsa-faraday-bench.png)
[PDF version](testbenches_doc_resources/5g-nsa-faraday-bench.pdf) | [LaTeX/TikZ version](testbenches_doc_resources/5g-nsa-faraday-bench.tex) if you want to modify to reflect your setup
### 4G Testbench(es)
**Purpose**: 4G/LTE testbenches
![4G Faraday Cage Testbench](testbenches_doc_resources/4g-faraday-bench.png)
[PDF version](testbenches_doc_resources/4g-faraday-bench.pdf) | [LaTeX/TikZ version](testbenches_doc_resources/4g-faraday-bench.tex) if you want to modify to reflect your setup
## Pipelines
### [RAN-Container-Parent](https://jenkins-oai.eurecom.fr/job/RAN-Container-Parent/)
**Purpose**: automatically triggered tests on MR creation or push, from Gitlab
Webhook
- [RAN-cppcheck](https://jenkins-oai.eurecom.fr/job/RAN-cppcheck/1664/)
- obelix
- performs static code analysis, currently not actively enforced
- [RAN-gNB-nrUE-MONO-TDD-Band78-N300](https://jenkins-oai.eurecom.fr/job/RAN-gNB-nrUE-MONO-TDD-Band78-N300/)
- caracal + N310
- pure performance test through phy-test scheduler, see command line for more
details
- [RAN-L2-Sim-Test-5G](https://jenkins-oai.eurecom.fr/job/RAN-L2-Sim-Test-5G/)
- obelix (gNB, 1x UE, OAI 5GC)
- L2simulator: skips physical layer and uses proxy between gNB and UE,
currently only ping
- [RAN-NSA-B200-Module-LTEBOX-Container](https://jenkins-oai.eurecom.fr/job/RAN-NSA-B200-Module-LTEBOX-Container/)
- obelix + B200, nepes + B200, idefix + Quectel, porcepix w/ ltebox
- basic NSA test, known to be instable
- [RAN-PhySim-Cluster](https://jenkins-oai.eurecom.fr/job/RAN-PhySim-Cluster/)
- asterix (`Asterix-OC-oaicicd-session` resource), tests in OpenShift Cluster
- unitary simulators (`nr_dlsim`, etc.)
- [RAN-RF-Sim-Test-4G](https://jenkins-oai.eurecom.fr/job/RAN-RF-Sim-Test-4G/)
- obelix (eNB, 1x UE, OAI EPC)
- uses RFsimulator, for FDD 5, 10, 20MHz with core, 5MHz noS1
- [RAN-RF-Sim-Test-5G](https://jenkins-oai.eurecom.fr/job/RAN-RF-Sim-Test-5G/)
- obelix (gNB, 2x UE, OAI 5GC)
- uses RFsimulator, TDD 40MHz, FDD 40MHz, F1 split
- [RAN-RHEL8-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-RHEL8-Image-Builder/)
- asterix: RHEL 8 image build using podman
- [RAN-Ubuntu18-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-Ubuntu18-Image-Builder/)
- obelix: Ubuntu 18 image build using docker
### [RAN-CI-NSA-Trigger](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-CI-NSA-Trigger/)
**Purpose**: longer-running over-the-air LTE, NSA, and SA tests, triggered
through cron job at midnight
- [RAN-LTE-2x2-Module-OAIEPC](https://jenkins-oai.eurecom.fr/job/RAN-LTE-2x2-Module-OAIEPC/)
- obelix + N310, nrmodule2 + Quectel, porcepix w/ Magma EPC
- LTE 2x2 test with TM1 and TM2
- [RAN-NSA-B200-Module-LTEBOX](https://jenkins-oai.eurecom.fr/job/RAN-NSA-B200-Module-LTEBOX/)
- obelix + B200 (eNB), nepes + B200 (gNB), idefix + Quectel, porcepix w/ ltebox
- Note: like [RAN-NSA-B200-Module-LTEBOX-Container](https://jenkins-oai.eurecom.fr/job/RAN-NSA-B200-Module-LTEBOX-Container/) above, but compiled/run from source
- [RAN-NSA-2x2-Module-OAIEPC](https://jenkins-oai.eurecom.fr/job/RAN-NSA-2x2-Module-OAIEPC/)
- obelix + N310 (eNB), asterix + N310 (gNB), nrmodule2 + Quectel, porcepix w/ Magma EPC
- LTE 2x2 and NR 2x2 (non-standalone)
- [RAN-SA-Module-CN5G](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-SA-Module-CN5G/)
- asterix + N310 (gNB), nrmodule2 + Quectel, porcepix w/ OAI 5GC
- NR 2x2 (standalone)
- [RAN-SA-OAIUE-N310-X300-CN5G](https://jenkins-oai.eurecom.fr/job/RAN-SA-OAIUE-N310-X300-CN5G/)
- asterix + N310 (gNB), obelix + N310 or X300 (5G UE), porcepix w/ OAI 5GC
- OTA test with OAIUE using both N310 and X300
- [RAN-SA-AmariS-CN5G](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-SA-AmariS-CN5G/)
- asterix + N310, amariue (1x UE), porcepix w/ OAI 5GC
- Amarisoft UE simulator: expected to be increased to more UEs
### [RAN-CI-develop](https://open5glab.eurecom.fr:8083/jenkins/job/RAN-CI-develop/)
**Purpose**: automatically triggered tests, mostly 4G, to be phased out and
integrated into RAN-Container-Parent
runs tests:
- bellatrix: runs 4G/5G simulators directly (eNB + 1x UE + (opt.) OAI EPC, gNB + 1x UE in "noS1")
triggers pipelines:
- [eNB-CI-F1-FDD-Band7-B210](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-CI-F1-FDD-Band7-B210/)
- hutch + B210, nano w/ ltebox + 2x UE
- tests 4G FDD with F1 split, 5MHz, 10MHz, 20MHz. 5MHz stable, rest known to
be unstable
- [eNB-CI-FDD-Band7-B210](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-CI-FDD-Band7-B210/)
- hutch + B210, nano w/ ltebox + 2x UE
- tests T tracer, information through FlexRAN, RRC inactivity timers,
inactivity timers + FlexRAN, different bandwidths
- [eNB-CI-IF4p5-FDD-Band7-B210](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-CI-IF4p5-FDD-Band7-B210/)
- hutch + B210, nano w/ ltebox + 2x UE
- tests IF4.5 split over bandwidths 5, 10, 20 MHz in Band 7
- [eNB-CI-IF4p5-TDD-Band40-B210](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-CI-IF4p5-TDD-Band40-B210/)
- starsky + B210, nano w/ ltebox + 2x UE
- tests IF4.5 split over bandwidths 5, 10, 20 MHz in Band 40
- [eNB-CI-TDD-Band40-B210](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-CI-TDD-Band40-B210/)
- starsky + B210, nano w/ ltebox + 2x UE
- T tracer, TM1 over bandwidths 5, 10, 20 MHz in Band 40, default scheduler
for 20 MHz
- [eNB-UE-CI-MONO-FDD-Band7-B200](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-UE-CI-MONO-FDD-Band7-B200/)
- hutch + B210 (eNB), carabe + B210 (4G UE), nano w/ ltebox
- tests OAI 4G for 5 MHz/TM1 with both CN and in noS1 model, MBMS; known to
be unstable
- [UE-CI-FDD-Band20-B200](https://open5glab.eurecom.fr:8083/jenkins/job/UE-CI-FDD-Band20-B200/)
- starsky + B210 (sniffer)
- Sniff MIB + SIB1 of Orange, SFR
......@@ -50,7 +50,7 @@ At the moment of writing this document interoperability with the following COTS
## 1.1 gNB build and configuration
To get the code and build the gNB executable:
### Ubuntu 18.04
### Build gNB
```bash
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git
git checkout develop
......@@ -61,42 +61,6 @@ To get the code and build the gNB executable:
./build_oai --gNB -w USRP
```
### Ubuntu 20.04
```bash
# Build UHD from source
# https://files.ettus.com/manual/page_build_guide.html
sudo apt-get install libboost-all-dev libusb-1.0-0-dev doxygen python3-docutils python3-mako python3-numpy python3-requests python3-ruamel.yaml python3-setuptools cmake build-essential
git clone https://github.com/EttusResearch/uhd.git
cd uhd/host
mkdir build
cd build
cmake ../
make -j 4
make test # This step is optional
sudo make install
sudo ldconfig
sudo uhd_images_downloader
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git
git checkout develop
# Install dependencies in Ubuntu 20.04
cd
cd openairinterface5g/
source oaienv
cd cmake_targets/
./install_external_packages.ubuntu20
# Build OAI gNB
cd
cd openairinterface5g/
source oaienv
cd cmake_targets/
./build_oai --gNB -w USRP
```
A reference configuration file for the **monolithic** gNB is provided [here](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf).
......
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{backgrounds, positioning, shapes.symbols}
\usepackage{helvet}
\renewcommand*{\rmdefault}{\sfdefault}
\begin{document}
\begin{tikzpicture}
[
font=\footnotesize,
faraday/.style={minimum size=3cm, draw, dashed},
duplexer/.style={draw,fill=white},
]
\node[faraday, label={[anchor=south west]above left:Faraday cage}] (faraday) {};
\node[above left=0cm and 2.8cm of faraday, label=above:hutch] (hutch)
{\includegraphics[width=1.2cm]{server}};
\node[right=0.3cm of hutch, label=above:B210] (b210h)
{\includegraphics[width=1.2cm]{b210}} edge (hutch);
\node[below left=0.35cm of faraday.north east] (anto)
{\includegraphics[width=0.3cm]{antenna}};
\draw (b210h) -| node [pos=0.25, duplexer] {B7} (anto);
\node[below left=0cm and 2.8cm of faraday, label=above:starsky] (starsky)
{\includegraphics[width=1.2cm]{server}};
\node[right=0.3cm of starsky, label=above:B210] (b210s)
{\includegraphics[width=1.2cm]{b210}} edge (starsky);
\draw (hutch) -- (b210h);
\node[above left=0.35cm of faraday.south east] (antn)
{\includegraphics[width=0.3cm]{antenna}};
\draw (b210s) -| node [pos=0.35, duplexer] {B40} (antn);
\node[below right=-0.2cm and 0.8cm of b210s] (antn2)
{\includegraphics[width=0.3cm]{antenna}};
\draw (b210s) -| (antn2);
\node[left=5cm of faraday, label=above:nano] (nano)
{\includegraphics[width=1.2cm]{server}};
\draw (hutch) -- (nano);
\draw (starsky) -- (nano);
\node[above right=0.0cm and 0.35cm of faraday.west] (phone1)
{\includegraphics[height=0.5cm]{phone}};
\node[below right=0.0cm and 0.35cm of faraday.west] (phone2)
{\includegraphics[height=0.5cm]{phone}};
\draw (nano) -- node[above] {USB/adb} +(5cm,0) |- (phone1);
\draw (nano) -- +(5cm,0) |- (phone2);
\node[right=1.5cm of faraday, label=above:B210] (b210c)
{\includegraphics[width=1.2cm]{b210}};
\node[left=.5cm of faraday.east] (antc)
{\includegraphics[width=0.3cm]{antenna}};
\draw (b210c) -- node [pos=0.35, duplexer] {B7UE} (antc);
\node[right=0.7cm of b210c, label=above:carabe] (carabe)
{\includegraphics[width=1.2cm]{server}}
edge (b210c);
\end{tikzpicture}
\end{document}
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{backgrounds, positioning, shapes.symbols}
\usepackage{helvet}
\renewcommand*{\rmdefault}{\sfdefault}
\begin{document}
\begin{tikzpicture}
[
font=\footnotesize,
faraday/.style={minimum size=3cm, draw, dashed},
duplexer/.style={draw,fill=white},
]
\node[faraday, label={[anchor=south east]above right:Faraday cage}] (faraday) {};
\node[above left=0cm and 2.8cm of faraday, label=above:obelix] (obelix)
{\includegraphics[width=1.2cm]{server}};
\node[right=0.3cm of obelix, label=above:B200-mini] (b210o)
{\includegraphics[width=1.2cm]{b200-mini}} edge (obelix);
\node[below right=0.35cm of faraday.north west] (anto)
{\includegraphics[width=0.3cm]{antenna}};
\draw (b210o) -| node [pos=0.2, duplexer] {B7} (anto);
\node[below left=0cm and 2.8cm of faraday, label=above:nepes] (nepes)
{\includegraphics[width=1.2cm]{server}};
\node[right=0.3cm of nepes, label=above:B200-mini] (b210n)
{\includegraphics[width=1.2cm]{b200-mini}} edge (nepes);
\draw (obelix) -- (b210o);
\node[above right=0.35cm of faraday.south west] (antn)
{\includegraphics[width=0.3cm]{antenna}};
\draw (b210n) -| node [pos=0.2, duplexer] {B78} (antn);
\node[left=5cm of faraday, label=above:porcepix] (porcepix)
{\includegraphics[width=1.2cm]{server}};
\draw (obelix) -- (porcepix);
\draw (nepes) -- (porcepix);
\node[right=1.5cm of faraday, label=above:RM500Q-GL] (quectel)
{\includegraphics[height=1.2cm]{quectel}};
\node[above left=-0.1cm and 0.2cm of faraday.east] (aq2)
{\includegraphics[width=0.3cm]{antenna}} edge (quectel);
\node[above=-0.2cm of aq2] (aq1)
{\includegraphics[width=0.3cm]{antenna}} edge (quectel);
\node[below=-0.2cm of aq2] (aq3)
{\includegraphics[width=0.3cm]{antenna}} edge (quectel);
\node[below=-0.2cm of aq3]
{\includegraphics[width=0.3cm]{antenna}} edge (quectel);
\node[right=1cm of quectel, label=above:idefix] (idefix)
{\includegraphics[width=1.2cm]{server}}
edge (quectel);
\end{tikzpicture}
\end{document}
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{backgrounds, positioning, shapes.symbols}
\usepackage{helvet}
\renewcommand*{\rmdefault}{\sfdefault}
\begin{document}
\begin{tikzpicture}
[
font=\footnotesize,
faraday/.style={minimum size=3cm, draw, dashed},
duplexer/.style={draw,fill=white},
]
\node[label=above:porcepix] (porcepix)
{\includegraphics[width=1.2cm]{server}};
\node[above right=1cm and 2cm of porcepix, label=above:asterix] (asterix)
{\includegraphics[width=1.2cm]{server}}
edge (porcepix);
\node[right=0.3cm of asterix, label=above:N310] (n310a)
{\includegraphics[width=1.5cm]{n310}} edge (asterix);
\node[right=.2cm of n310a, duplexer] (b78o) {B78} edge (n310a);
\node[below right=-0.1cm and 0.35cm of b78o.east] (anto1)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[above right=-0.1cm and 0.35cm of b78o.east] (anto2)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[right=2cm of porcepix, label=above:obelix] (obelix)
{\includegraphics[width=1.2cm]{server}}
edge (porcepix);
\node[above right=-0.5cm and 0.3cm of obelix, label=above:N310] (n310o)
{\includegraphics[width=1.5cm]{n310}} edge (obelix);
\node[right=.2cm of n310o, duplexer] (b78o) {B40} edge (n310o);
\node[below right=-0.1cm and 0.35cm of b78o.east] (anto1)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[above right=-0.1cm and 0.35cm of b78o.east] (anto2)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[below right=-0.5cm and 0.3cm of obelix, label=above:X310] (x310o)
{\includegraphics[width=1.5cm]{x310}} edge (obelix);
\node[right=.2cm of x310o, duplexer] (b78o) {B78} edge (x310o);
\node[below right=-0.1cm and 0.35cm of b78o.east] (anto1)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[above right=-0.1cm and 0.35cm of b78o.east] (anto2)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[right=5.0cm of n310o, label=above:RM500Q-GL] (quectel)
{\includegraphics[height=1.2cm]{quectel}};
\node[above left=-0.1cm and 0.8cm of quectel.west] (aq2)
{\includegraphics[width=0.3cm]{antenna}} edge (quectel);
\node[above=-0.2cm of aq2] (aq1)
{\includegraphics[width=0.3cm]{antenna}} edge (quectel);
\node[below=-0.2cm of aq2] (aq3)
{\includegraphics[width=0.3cm]{antenna}} edge (quectel);
\node[below=-0.2cm of aq3]
{\includegraphics[width=0.3cm]{antenna}} edge (quectel);
\node[right=1cm of quectel, label=above:nrmodule2] (nrmodule2)
{\includegraphics[width=1.2cm]{server}}
edge (quectel);
\end{tikzpicture}
\end{document}
......@@ -464,6 +464,7 @@ typedef struct {
uint8_t nEpreRatioOfPDSCHToPTRS;
/// MCS table for this DLSCH
uint8_t mcs_table;
uint32_t tbslbrm;
uint8_t nscid;
uint16_t dlDmrsScramblingId;
uint16_t pduBitmap;
......
......@@ -776,6 +776,10 @@ typedef struct {
nfapi_nr_dl_dci_pdu_t dci_pdu[MAX_DCI_CORESET];
} nfapi_nr_dl_tti_pdcch_pdu_rel15_t;
typedef struct {
uint32_t tbSizeLbrmBytes;
}nfapi_v3_pdsch_maintenance_parameters_t;
typedef struct {
uint16_t pduBitmap;
uint16_t rnti;
......@@ -854,6 +858,7 @@ typedef struct {
uint8_t nEpreRatioOfPDSCHToPTRS;
// Beamforming
nfapi_nr_tx_precoding_and_beamforming_t precodingAndBeamforming;
nfapi_v3_pdsch_maintenance_parameters_t maintenance_parms_v3;
}nfapi_nr_dl_tti_pdsch_pdu_rel15_t;
......@@ -1195,6 +1200,10 @@ typedef struct
#define PUSCH_PDU_BITMAP_PUSCH_PTRS 0x4
#define PUSCH_PDU_BITMAP_DFTS_OFDM 0x8
typedef struct {
uint32_t tbSizeLbrmBytes;
}nfapi_v3_pusch_maintenance_parameters_t;
typedef struct
{
uint16_t pdu_bit_map;//Bitmap indicating presence of optional PDUs (see above)
......@@ -1240,7 +1249,7 @@ typedef struct
nfapi_nr_dfts_ofdm_t dfts_ofdm;
//beamforming
nfapi_nr_ul_beamforming_t beamforming;
nfapi_v3_pdsch_maintenance_parameters_t maintenance_parms_v3;
} nfapi_nr_pusch_pdu_t;
//for pucch_pdu:
......
......@@ -481,16 +481,11 @@ uint32_t nr_compute_tbs(uint16_t Qm,
uint8_t tb_scaling,
uint8_t Nl);
uint32_t nr_compute_tbslbrm(uint16_t table,
uint16_t nb_rb,
uint8_t Nl);
void nr_interleaving_ldpc(uint32_t E, uint8_t Qm, uint8_t *e,uint8_t *f);
void nr_deinterleaving_ldpc(uint32_t E, uint8_t Qm, int16_t *e,int16_t *f);
int nr_rate_matching_ldpc(uint8_t Ilbrm,
uint32_t Tbslbrm,
int nr_rate_matching_ldpc(uint32_t Tbslbrm,
uint8_t BG,
uint16_t Z,
uint8_t *w,
......@@ -501,8 +496,7 @@ int nr_rate_matching_ldpc(uint8_t Ilbrm,
uint8_t rvidx,
uint32_t E);
int nr_rate_matching_ldpc_rx(uint8_t Ilbrm,
uint32_t Tbslbrm,
int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm,
uint8_t BG,
uint16_t Z,
int16_t *w,
......
......@@ -387,8 +387,7 @@ void nr_deinterleaving_ldpc(uint32_t E, uint8_t Qm, int16_t *e,int16_t *f)
}
int nr_rate_matching_ldpc(uint8_t Ilbrm,
uint32_t Tbslbrm,
int nr_rate_matching_ldpc(uint32_t Tbslbrm,
uint8_t BG,
uint16_t Z,
uint8_t *w,
......@@ -409,7 +408,7 @@ int nr_rate_matching_ldpc(uint8_t Ilbrm,
//Bit selection
N = (BG==1)?(66*Z):(50*Z);
if (Ilbrm == 0)
if (Tbslbrm == 0)
Ncb = N;
else {
Nref = 3*Tbslbrm/(2*C); //R_LBRM = 2/3
......@@ -419,11 +418,11 @@ int nr_rate_matching_ldpc(uint8_t Ilbrm,
ind = (index_k0[BG-1][rvidx]*Ncb/N)*Z;
#ifdef RM_DEBUG
printf("nr_rate_matching_ldpc: E %d, F %d, Foffset %d, k0 %d, Ncb %d, rvidx %d, Ilbrm %d\n", E, F, Foffset,ind, Ncb, rvidx, Ilbrm);
printf("nr_rate_matching_ldpc: E %d, F %d, Foffset %d, k0 %d, Ncb %d, rvidx %d, Tbslbrm %d\n", E, F, Foffset,ind, Ncb, rvidx, Tbslbrm);
#endif
if (Foffset > E) {
LOG_E(PHY,"nr_rate_matching: invalid parameters (Foffset %d > E %d) F %d, k0 %d, Ncb %d, rvidx %d, Ilbrm %d\n",Foffset,E,F, ind, Ncb, rvidx, Ilbrm);
LOG_E(PHY,"nr_rate_matching: invalid parameters (Foffset %d > E %d) F %d, k0 %d, Ncb %d, rvidx %d, Tbslbrm %d\n",Foffset,E,F, ind, Ncb, rvidx, Tbslbrm);
return -1;
}
if (Foffset > Ncb) {
......@@ -471,8 +470,7 @@ int nr_rate_matching_ldpc(uint8_t Ilbrm,
return 0;
}
int nr_rate_matching_ldpc_rx(uint8_t Ilbrm,
uint32_t Tbslbrm,
int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm,
uint8_t BG,
uint16_t Z,
int16_t *w,
......@@ -498,7 +496,7 @@ int nr_rate_matching_ldpc_rx(uint8_t Ilbrm,
//Bit selection
N = (BG==1)?(66*Z):(50*Z);
if (Ilbrm == 0)
if (Tbslbrm == 0)
Ncb = N;
else {
Nref = (3*Tbslbrm/(2*C)); //R_LBRM = 2/3
......@@ -516,7 +514,7 @@ int nr_rate_matching_ldpc_rx(uint8_t Ilbrm,
}
#ifdef RM_DEBUG
printf("nr_rate_matching_ldpc_rx: Clear %d, E %d, k0 %d, Ncb %d, rvidx %d, Ilbrm %d\n", clear, E, ind, Ncb, rvidx, Ilbrm);
printf("nr_rate_matching_ldpc_rx: Clear %d, E %d, k0 %d, Ncb %d, rvidx %d, Tbslbrm %d\n", clear, E, ind, Ncb, rvidx, Tbslbrm);
#endif
if (clear==1) memset(w,0,Ncb*sizeof(int16_t));
......
......@@ -240,19 +240,12 @@ void ldpc8blocks( void *p) {
impp->F,
Kr-impp->F-2*(*impp->Zc),
mod_order,nb_rb,rel15->nrOfLayers);
// for tbslbrm calculation according to 5.4.2.1 of 38.212
uint8_t Nl = 4;
if (rel15->nrOfLayers < Nl)
Nl = rel15->nrOfLayers;
uint32_t Tbslbrm = nr_compute_tbslbrm(rel15->mcsTable[0],nb_rb,Nl);
uint8_t Ilbrm = 1;
uint32_t Tbslbrm = rel15->maintenance_parms_v3.tbSizeLbrmBytes;
uint8_t e[E];
bzero (e, E);
nr_rate_matching_ldpc(Ilbrm,
Tbslbrm,
nr_rate_matching_ldpc(Tbslbrm,
impp->BG,
*impp->Zc,
impp->d[rr],
......
......@@ -234,7 +234,6 @@ void nr_processULSegment(void* arg) {
int rv_index = rdata->rv_index;
int r_offset = rdata->r_offset;
uint8_t kc = rdata->Kc;
uint32_t Tbslbrm = rdata->Tbslbrm;
short* ulsch_llr = rdata->ulsch_llr;
int max_ldpc_iterations = p_decoderParms->numMaxIter;
int8_t llrProcBuf[OAI_UL_LDPC_MAX_NUM_LLR] __attribute__ ((aligned(32)));
......@@ -244,8 +243,6 @@ void nr_processULSegment(void* arg) {
__m128i *pv = (__m128i*)&z;
__m128i *pl = (__m128i*)&l;
uint8_t Ilbrm = 0;
Kr = ulsch_harq->K;
Kr_bytes = Kr>>3;
......@@ -293,8 +290,7 @@ void nr_processULSegment(void* arg) {
//start_meas(&phy_vars_gNB->ulsch_rate_unmatching_stats);
if (nr_rate_matching_ldpc_rx(Ilbrm,
Tbslbrm,
if (nr_rate_matching_ldpc_rx(rdata->tbslbrm,
p_decoderParms->BG,
p_decoderParms->Z,
ulsch_harq->d[r],
......@@ -394,7 +390,6 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
uint32_t r_offset;
uint32_t offset;
int kc;
int Tbslbrm;
int E;
#ifdef PRINT_CRC_CHECK
......@@ -539,7 +534,6 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
if (!frame%100)
printf("K %d C %d Z %d \n", harq_process->K, harq_process->C, harq_process->Z);
#endif
Tbslbrm = nr_compute_tbslbrm(0,nb_rb,n_layers);
p_decParams->Z = harq_process->Z;
......@@ -590,10 +584,10 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
rdata->r_offset = r_offset;
rdata->Kr_bytes = Kr_bytes;
rdata->rv_index = pusch_pdu->pusch_data.rv_index;
rdata->Tbslbrm = Tbslbrm;
rdata->offset = offset;
rdata->ulsch = ulsch;
rdata->ulsch_id = ULSCH_id;
rdata->tbslbrm = pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes;
pushTpool(phy_vars_gNB->threadPool,req);
phy_vars_gNB->nbDecode++;
LOG_D(PHY,"Added a block to decode, in pipe: %d\n",phy_vars_gNB->nbDecode);
......
......@@ -289,8 +289,6 @@ void nr_processDLSegment(void* arg) {
__m128i *pv = (__m128i*)&z;
__m128i *pl = (__m128i*)&l;
uint8_t Ilbrm = 1;
Kr = harq_process->K; // [hna] overwrites this line "Kr = p_decParams->Z*kb"
Kr_bytes = Kr>>3;
K_bits_F = Kr-harq_process->F;
......@@ -323,8 +321,7 @@ void nr_processDLSegment(void* arg) {
harq_process->round); */
//VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_RATE_MATCHING, VCD_FUNCTION_IN);
if (nr_rate_matching_ldpc_rx(Ilbrm,
Tbslbrm,
if (nr_rate_matching_ldpc_rx(Tbslbrm,
p_decoderParms->BG,
p_decoderParms->Z,
harq_process->d[r],
......@@ -451,7 +448,6 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
phy_vars_ue->dl_stats[harq_process->DLround]++;
LOG_D(PHY,"Round %d RV idx %d\n",harq_process->DLround,harq_process->rvidx);
uint8_t kc;
uint32_t Tbslbrm;// = 950984;
uint16_t nb_rb;// = 30;
double Coderate;// = 0.0;
uint8_t dmrs_Type = harq_process->dmrsConfigType;
......@@ -567,10 +563,6 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
if (LOG_DEBUGFLAG(DEBUG_DLSCH_DECOD) && (!frame%100))
LOG_I(PHY,"K %d C %d Z %d nl %d \n", harq_process->K, harq_process->C, p_decParams->Z, harq_process->Nl);
}
if ((harq_process->Nl)<4)
Tbslbrm = nr_compute_tbslbrm(harq_process->mcs_table,nb_rb,harq_process->Nl);
else
Tbslbrm = nr_compute_tbslbrm(harq_process->mcs_table,nb_rb,4);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_SEGMENTATION, VCD_FUNCTION_OUT);
p_decParams->Z = harq_process->Z;
......@@ -621,7 +613,7 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
rdata->r_offset = r_offset;
rdata->Kr_bytes = Kr_bytes;
rdata->rv_index = harq_process->rvidx;
rdata->Tbslbrm = Tbslbrm;
rdata->Tbslbrm = harq_process->tbslbrm;
rdata->offset = offset;
rdata->dlsch = dlsch;
rdata->dlsch_id = 0;
......
......@@ -260,6 +260,7 @@ typedef struct {
uint16_t ptrs_symbols;
// PTRS symbol index, to be updated every PTRS symbol within a slot.
uint8_t ptrs_symbol_index;
uint32_t tbslbrm;
uint8_t nscid;
uint16_t dlDmrsScramblingId;
/// PDU BITMAP
......
......@@ -171,8 +171,8 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
NR_UE_ULSCH_t *ulsch,
NR_DL_FRAME_PARMS* frame_parms,
uint8_t harq_pid,
unsigned int G)
{
unsigned int G) {
start_meas(&ue->ulsch_encoding_stats);
/////////////////////////parameters and variables initialization/////////////////////////
......@@ -188,9 +188,8 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
uint16_t Kr=0;
uint32_t r_offset=0;
uint32_t F=0;
uint8_t Ilbrm = 0;
uint32_t Tbslbrm = 950984; //max tbs
float Coderate = 0.0;
///////////
/////////////////////////////////////////////////////////////////////////////////////////
......@@ -376,12 +375,9 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
uint32_t E = nr_get_E(G, harq_process->C, mod_order, harq_process->pusch_pdu.nrOfLayers, r);
Tbslbrm = nr_compute_tbslbrm(0,nb_rb,harq_process->pusch_pdu.nrOfLayers);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RATE_MATCHING_LDPC, VCD_FUNCTION_IN);
start_meas(&ue->ulsch_rate_matching_stats);
if (nr_rate_matching_ldpc(Ilbrm,
Tbslbrm,
if (nr_rate_matching_ldpc(0,
harq_process->BG,
*pz,
harq_process->d[r],
......
......@@ -935,8 +935,8 @@ typedef struct LDPCDecode_s {
int segment_r;
int r_offset;
int offset;
int Tbslbrm;
int decodeIterations;
uint32_t tbslbrm;
} ldpcDecode_t;
struct ldpcReqId {
......
......@@ -315,6 +315,7 @@ void configure_dlsch(NR_UE_DLSCH_t *dlsch0,
dlsch0_harq->mcs = dlsch_config_pdu->mcs;
dlsch0_harq->rvidx = dlsch_config_pdu->rv;
dlsch0->g_pucch = dlsch_config_pdu->accumulated_delta_PUCCH;
dlsch0_harq->tbslbrm = dlsch_config_pdu->tbslbrm;
dlsch0_harq->nscid = dlsch_config_pdu->nscid;
dlsch0_harq->dlDmrsScramblingId = dlsch_config_pdu->dlDmrsScramblingId;
//get nrOfLayers from DCI info
......@@ -341,6 +342,7 @@ void configure_dlsch(NR_UE_DLSCH_t *dlsch0,
LOG_D(MAC, ">>>> \tdlsch0->g_pucch = %d\tdlsch0_harq.mcs = %d\n", dlsch0->g_pucch, dlsch0_harq->mcs);
}
int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){
bool found = false;
......
......@@ -119,6 +119,7 @@ int main(int argc, char **argv)
double DS_TDL = .03;
cpuf = get_cpu_freq_GHz();
char gNBthreads[128]="n";
int Tbslbrm = 950984;
if (load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY) == 0) {
exit_fun("[NR_DLSCHSIM] Error, configuration module init failed\n");
......@@ -457,6 +458,7 @@ int main(int argc, char **argv)
rel15->dlDmrsSymbPos = 4;
rel15->mcsIndex[0] = Imcs;
rel15->numDmrsCdmGrpsNoData = 1;
rel15->maintenance_parms_v3.tbSizeLbrmBytes = Tbslbrm;
double modulated_input[16 * 68 * 384]; // [hna] 16 segments, 68*Zc
short channel_output_fixed[16 * 68 * 384];
//unsigned char *estimated_output;
......@@ -476,6 +478,7 @@ int main(int argc, char **argv)
harq_process->dmrsConfigType = NFAPI_NR_DMRS_TYPE1;
harq_process->dlDmrsSymbPos = 4;
harq_process->n_dmrs_cdm_groups = 1;
harq_process->tbslbrm = Tbslbrm;
printf("harq process ue mcs = %d Qm = %d, symb %d\n", harq_process->mcs, harq_process->Qm, nb_symb_sch);
unsigned char *test_input=dlsch->harq_process.pdu;
......
......@@ -92,12 +92,10 @@ uint32_t nr_compute_tbs(uint16_t Qm,
}
//tbslbrm calculation according to 5.4.2.1 of 38.212
uint32_t nr_compute_tbslbrm(uint16_t table,
uint16_t nb_rb,
uint8_t Nl)
{
uint8_t Nl) {
uint16_t R, nb_re;
uint16_t nb_rb_lbrm=0;
......@@ -122,34 +120,31 @@ uint32_t nr_compute_tbslbrm(uint16_t table,
Ninfo = (nb_re * R * Qm * Nl)>>10;
if (Ninfo <=3824) {
n = max(3, floor(log2(Ninfo)) - 6);
Np_info = max(24, (Ninfo>>n)<<n);
for (int i=0; i<INDEX_MAX_TBS_TABLE; i++) {
if (Tbstable_nr[i] >= Np_info){
nr_tbs = Tbstable_nr[i];
break;
}
}
n = max(3, floor(log2(Ninfo)) - 6);
Np_info = max(24, (Ninfo>>n)<<n);
for (int i=0; i<INDEX_MAX_TBS_TABLE; i++) {
if (Tbstable_nr[i] >= Np_info){
nr_tbs = Tbstable_nr[i];
break;
}
}
}
else {
n = log2(Ninfo-24)-5;
Np_info = max(3840, (ROUNDIDIV((Ninfo-24),(1<<n)))<<n);
if (R <= 256) {
C = CEILIDIV((Np_info+24),3816);
nr_tbs = (C<<3)*CEILIDIV((Np_info+24),(C<<3)) - 24;
}
else {
if (Np_info > 8424){
C = CEILIDIV((Np_info+24),8424);
nr_tbs = (C<<3)*CEILIDIV((Np_info+24),(C<<3)) - 24;
}
else {
nr_tbs = ((CEILIDIV((Np_info+24),8))<<3) - 24;
}
}
n = log2(Ninfo-24)-5;
Np_info = max(3840, (ROUNDIDIV((Ninfo-24),(1<<n)))<<n);
if (R <= 256) {
C = CEILIDIV((Np_info+24),3816);
nr_tbs = (C<<3)*CEILIDIV((Np_info+24),(C<<3)) - 24;
}
else {
if (Np_info > 8424){
C = CEILIDIV((Np_info+24),8424);
nr_tbs = (C<<3)*CEILIDIV((Np_info+24),(C<<3)) - 24;
}
else
nr_tbs = ((CEILIDIV((Np_info+24),8))<<3) - 24;
}
}
return nr_tbs;
}
......@@ -4087,6 +4087,25 @@ uint16_t compute_pucch_prb_size(uint8_t format,
}
}
int get_bw_tbslbrm(NR_BWP_t *genericParameters,
NR_CellGroupConfig_t *cg) {
int bw = 0;
if (cg && cg->spCellConfig && cg->spCellConfig->spCellConfigDedicated &&
cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) {
struct NR_ServingCellConfig__downlinkBWP_ToAddModList *BWP_list = cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList;
for (int i=0; i<BWP_list->list.count; i++) {
genericParameters = &BWP_list->list.array[i]->bwp_Common->genericParameters;
int curr_bw = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE);
if (curr_bw > bw)
bw = curr_bw;
}
}
else
bw = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE);
return bw;
}
/* extract UL PTRS values from RRC and validate it based upon 38.214 6.2.3 */
bool set_ul_ptrs_values(NR_PTRS_UplinkConfig_t *ul_ptrs_config,
uint16_t rbSize,uint8_t mcsIndex, uint8_t mcsTable,
......
......@@ -122,6 +122,13 @@ uint8_t get_K_ptrs(uint16_t nrb0, uint16_t nrb1, uint16_t N_RB);
uint16_t get_nr_srs_offset(NR_SRS_PeriodicityAndOffset_t periodicityAndOffset);
int get_bw_tbslbrm(NR_BWP_t *genericParameters,
NR_CellGroupConfig_t *cg);
uint32_t nr_compute_tbslbrm(uint16_t table,
uint16_t nb_rb,
uint8_t Nl);
void get_type0_PDCCH_CSS_config_parameters(NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config,
frame_t frameP,
NR_MIB_t *mib,
......
......@@ -918,6 +918,19 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr
LOG_W(MAC, "[%d.%d] MCS value %d out of bounds! Possibly due to false DCI. Ignoring DCI!\n", frame, slot, dlsch_config_pdu_1_0->mcs);
return -1;
}
int bw_tbslbrm;
if (mac->scc || mac->scc_SIB || mac->cg) {
NR_BWP_t genericParameters = mac->scc ? mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters :
mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters;
bw_tbslbrm = get_bw_tbslbrm(&genericParameters, mac->cg);
}
else
bw_tbslbrm = dlsch_config_pdu_1_0->BWPSize;
dlsch_config_pdu_1_0->tbslbrm = nr_compute_tbslbrm(dlsch_config_pdu_1_0->mcs_table,
bw_tbslbrm,
1);
/* NDI (only if CRC scrambled by C-RNTI or CS-RNTI or new-RNTI or TC-RNTI)*/
dlsch_config_pdu_1_0->ndi = dci->ndi;
/* RV (only if CRC scrambled by C-RNTI or CS-RNTI or new-RNTI or TC-RNTI)*/
......@@ -1323,6 +1336,17 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr
dl_config->number_pdus = dl_config->number_pdus + 1;
/* TODO same calculation for MCS table as done in UL */
dlsch_config_pdu_1_1->mcs_table = (pdsch_Config->mcs_Table) ? (*pdsch_Config->mcs_Table + 1) : 0;
// TBS_LBRM according to section 5.4.2.1 of 38.212
long *maxMIMO_Layers = mac->cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->ext1->maxMIMO_Layers;
AssertFatal (maxMIMO_Layers != NULL,"Option with max MIMO layers not configured is not supported\n");
int nl_tbslbrm = *maxMIMO_Layers < 4 ? *maxMIMO_Layers : 4;
NR_BWP_t genericParameters = mac->scc ? mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters :
mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters;
int bw_tbslbrm = get_bw_tbslbrm(&genericParameters, mac->cg);
dlsch_config_pdu_1_1->tbslbrm = nr_compute_tbslbrm(dlsch_config_pdu_1_1->mcs_table,
bw_tbslbrm,
nl_tbslbrm);
/*PTRS configuration */
dlsch_config_pdu_1_1->pduBitmap = 0;
if(pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS != NULL) {
......
......@@ -1424,6 +1424,11 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra
pdsch_pdu_rel15->TBSize[0] = TBS;
}
int bw_tbslbrm = get_bw_tbslbrm(genericParameters, ra->CellGroup);
pdsch_pdu_rel15->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(mcsTableIdx,
bw_tbslbrm,
1);
// Fill PDCCH DL DCI PDU
nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci];
pdcch_pdu_rel15->numDlDci++;
......@@ -1801,6 +1806,11 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra
int x_Overhead = 0;
nr_get_tbs_dl(&dl_tti_pdsch_pdu->pdsch_pdu, x_Overhead, pdsch_pdu_rel15->numDmrsCdmGrpsNoData, tb_scaling);
int bw_tbslbrm = get_bw_tbslbrm(genericParameters, ra->CellGroup);
pdsch_pdu_rel15->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(mcsTableIdx,
bw_tbslbrm,
1);
pdsch_pdu_rel15->precodingAndBeamforming.num_prgs=1;
pdsch_pdu_rel15->precodingAndBeamforming.prg_size=275;
pdsch_pdu_rel15->precodingAndBeamforming.dig_bf_interfaces=1;
......
......@@ -504,6 +504,10 @@ void nr_fill_nfapi_dl_sib1_pdu(int Mod_idP,
LOG_D(NR_MAC,"sib1:rbStart %d, rbSize %d\n",pdsch_pdu_rel15->rbStart,pdsch_pdu_rel15->rbSize);
LOG_D(NR_MAC,"sib1:dlDmrsSymbPos = 0x%x\n", pdsch_pdu_rel15->dlDmrsSymbPos);
pdsch_pdu_rel15->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(0,
pdsch_pdu_rel15->BWPSize,
1);
/* Fill PDCCH DL DCI PDU */
nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci];
pdcch_pdu_rel15->numDlDci++;
......
......@@ -1259,6 +1259,17 @@ void nr_schedule_ue_spec(module_id_t module_id,
// Resource Allocation in time domain
pdsch_pdu->StartSymbolIndex = ps->startSymbolIndex;
pdsch_pdu->NrOfSymbols = ps->nrOfSymbols;
// TBS_LBRM according to section 5.4.2.1 of 38.212
long *maxMIMO_Layers = cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->ext1->maxMIMO_Layers;
AssertFatal (maxMIMO_Layers != NULL,"Option with max MIMO layers not configured is not supported\n");
int nl_tbslbrm = *maxMIMO_Layers < 4 ? *maxMIMO_Layers : 4;
// Maximum number of PRBs across all configured DL BWPs
int bw_tbslbrm = get_bw_tbslbrm(genericParameters, cg);
pdsch_pdu->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(ps->mcsTableIdx,
bw_tbslbrm,
nl_tbslbrm);
NR_PDSCH_Config_t *pdsch_Config=NULL;
if (bwp &&
......@@ -1312,7 +1323,7 @@ void nr_schedule_ue_spec(module_id_t module_id,
dci_pdu_rel15_t dci_payload;
memset(&dci_payload, 0, sizeof(dci_pdu_rel15_t));
// bwp indicator
const int n_dl_bwp = bwp ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count : 0;
const int n_dl_bwp = bwp ? cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count : 0;
AssertFatal(n_dl_bwp <= 1, "downlinkBWP_ToAddModList has %d BWP!\n", n_dl_bwp);
// as per table 7.3.1.1.2-1 in 38.212
dci_payload.bwp_indicator.val = bwp ? (n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1) : 0;
......@@ -1361,7 +1372,7 @@ void nr_schedule_ue_spec(module_id_t module_id,
const int rnti_type = NR_RNTI_C;
fill_dci_pdu_rel15(scc,
UE_info->CellGroup[UE_id],
cg,
dci_pdu,
&dci_payload,
dci_format,
......
......@@ -1833,6 +1833,8 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot)
pusch_pdu->pusch_data.tb_size = sched_pusch->tb_size;
pusch_pdu->pusch_data.num_cb = 0; //CBG not supported
pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes = 0;
LOG_D(NR_MAC,"PUSCH PDU : data_scrambling_identity %x, dmrs_scrambling_id %x\n",pusch_pdu->data_scrambling_id,pusch_pdu->ul_dmrs_scrambling_id);
/* TRANSFORM PRECODING --------------------------------------------------------*/
......
......@@ -1412,39 +1412,9 @@ void fill_initial_SpCellConfig(int uid,
NR_CSI_MeasConfig_t *csi_MeasConfig = calloc(1,sizeof(*csi_MeasConfig));
SpCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup = csi_MeasConfig;
int pdsch_AntennaPorts = configuration->pdsch_AntennaPorts.N1 * configuration->pdsch_AntennaPorts.N2 * configuration->pdsch_AntennaPorts.XP;
if (pdsch_AntennaPorts > 1) {
csi_MeasConfig->csi_IM_ResourceToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceToAddModList));
NR_CSI_IM_Resource_t *imres0 = calloc(1,sizeof(*imres0));
imres0->csi_IM_ResourceId = 0;
imres0->csi_IM_ResourceElementPattern = calloc(1,sizeof(*imres0->csi_IM_ResourceElementPattern));
imres0->csi_IM_ResourceElementPattern->present = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern_PR_pattern1;
imres0->csi_IM_ResourceElementPattern->choice.pattern1 = calloc(1,sizeof(*imres0->csi_IM_ResourceElementPattern->choice.pattern1));
imres0->csi_IM_ResourceElementPattern->choice.pattern1->subcarrierLocation_p1 = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern__pattern1__subcarrierLocation_p1_s4;
imres0->csi_IM_ResourceElementPattern->choice.pattern1->symbolLocation_p1 = 6;
imres0->freqBand = calloc(1,sizeof(*imres0->freqBand));
imres0->freqBand->startingRB = 0;
imres0->freqBand->nrofRBs = 108;
imres0->periodicityAndOffset = calloc(1,sizeof(*imres0->periodicityAndOffset));
imres0->periodicityAndOffset->present = NR_CSI_ResourcePeriodicityAndOffset_PR_slots320;
imres0->periodicityAndOffset->choice.slots320 = 0;
ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceToAddModList->list,imres0);
csi_MeasConfig->csi_IM_ResourceSetToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceSetToAddModList));
NR_CSI_IM_ResourceSet_t *imset0 = calloc(1,sizeof(*imset0));
imset0->csi_IM_ResourceSetId = 0;
NR_CSI_IM_ResourceId_t *res0 = calloc(1,sizeof(*res0));
*res0 = 0;
ASN_SEQUENCE_ADD(&imset0->csi_IM_Resources,res0);
ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceSetToAddModList->list,imset0);
}
else {
csi_MeasConfig->csi_IM_ResourceToAddModList = NULL;
csi_MeasConfig->csi_IM_ResourceSetToAddModList = NULL;
}
csi_MeasConfig->csi_IM_ResourceToReleaseList = NULL;
csi_MeasConfig->csi_IM_ResourceSetToReleaseList = NULL;
config_csirs(scc, csi_MeasConfig, uid, pdsch_AntennaPorts,curr_bwp,configuration->do_CSIRS);
config_csirs(scc, csi_MeasConfig, uid, pdsch_AntennaPorts, curr_bwp, configuration->do_CSIRS);
config_csiim(configuration->do_CSIRS, pdsch_AntennaPorts, curr_bwp, csi_MeasConfig);
csi_MeasConfig->csi_SSB_ResourceSetToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_SSB_ResourceSetToAddModList));
csi_MeasConfig->csi_SSB_ResourceSetToReleaseList = NULL;
......
......@@ -170,6 +170,103 @@ void config_csirs(NR_ServingCellConfigCommon_t *servingcellconfigcommon,
csi_MeasConfig->nzp_CSI_RS_ResourceToReleaseList = NULL;
}
void set_csiim_offset(struct NR_CSI_ResourcePeriodicityAndOffset *periodicityAndOffset,
struct NR_CSI_ResourcePeriodicityAndOffset *target_periodicityAndOffset) {
switch(periodicityAndOffset->present) {
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots4:
periodicityAndOffset->choice.slots4 = target_periodicityAndOffset->choice.slots4;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots5:
periodicityAndOffset->choice.slots5 = target_periodicityAndOffset->choice.slots5;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots8:
periodicityAndOffset->choice.slots8 = target_periodicityAndOffset->choice.slots8;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots10:
periodicityAndOffset->choice.slots10 = target_periodicityAndOffset->choice.slots10;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots16:
periodicityAndOffset->choice.slots16 = target_periodicityAndOffset->choice.slots16;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots20:
periodicityAndOffset->choice.slots20 = target_periodicityAndOffset->choice.slots20;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots32:
periodicityAndOffset->choice.slots32 = target_periodicityAndOffset->choice.slots32;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots40:
periodicityAndOffset->choice.slots40 = target_periodicityAndOffset->choice.slots40;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots64:
periodicityAndOffset->choice.slots64 = target_periodicityAndOffset->choice.slots64;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots80:
periodicityAndOffset->choice.slots80 = target_periodicityAndOffset->choice.slots80;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots160:
periodicityAndOffset->choice.slots160 = target_periodicityAndOffset->choice.slots160;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots320:
periodicityAndOffset->choice.slots320 = target_periodicityAndOffset->choice.slots320;
break;
case NR_CSI_ResourcePeriodicityAndOffset_PR_slots640:
periodicityAndOffset->choice.slots640 = target_periodicityAndOffset->choice.slots640;
break;
default:
AssertFatal(1==0,"CSI periodicity not among allowed values\n");
}
}
void config_csiim(int do_csirs, int dl_antenna_ports, int curr_bwp,
NR_CSI_MeasConfig_t *csi_MeasConfig) {
if (do_csirs && dl_antenna_ports > 1) {
csi_MeasConfig->csi_IM_ResourceToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceToAddModList));
NR_CSI_IM_Resource_t *imres = calloc(1,sizeof(*imres));
imres->csi_IM_ResourceId = 0;
NR_NZP_CSI_RS_Resource_t *nzpcsi = NULL;
for (int i=0; i<csi_MeasConfig->nzp_CSI_RS_ResourceToAddModList->list.count; i++){
nzpcsi = csi_MeasConfig->nzp_CSI_RS_ResourceToAddModList->list.array[i];
if (nzpcsi->nzp_CSI_RS_ResourceId == imres->csi_IM_ResourceId)
break;
}
AssertFatal(nzpcsi->nzp_CSI_RS_ResourceId == imres->csi_IM_ResourceId, "Couldn't find NZP CSI-RS corresponding to CSI-IM\n");
imres->csi_IM_ResourceElementPattern = calloc(1,sizeof(*imres->csi_IM_ResourceElementPattern));
imres->csi_IM_ResourceElementPattern->present = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern_PR_pattern1;
imres->csi_IM_ResourceElementPattern->choice.pattern1 = calloc(1,sizeof(*imres->csi_IM_ResourceElementPattern->choice.pattern1));
// starting subcarrier is 4 in the following configuration
// this is ok for current possible CSI-RS configurations (using only the first 4 symbols)
// TODO needs a more dynamic setting if CSI-RS is changed
imres->csi_IM_ResourceElementPattern->choice.pattern1->subcarrierLocation_p1 = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern__pattern1__subcarrierLocation_p1_s4;
imres->csi_IM_ResourceElementPattern->choice.pattern1->symbolLocation_p1 = nzpcsi->resourceMapping.firstOFDMSymbolInTimeDomain; // same symbol as CSI-RS
imres->freqBand = calloc(1,sizeof(*imres->freqBand));
imres->freqBand->startingRB = 0;
imres->freqBand->nrofRBs = ((curr_bwp>>2)+(curr_bwp%4>0))<<2;
imres->periodicityAndOffset = calloc(1,sizeof(*imres->periodicityAndOffset));
// same period and offset of the associated CSI-RS
imres->periodicityAndOffset->present = nzpcsi->periodicityAndOffset->present;
set_csiim_offset(imres->periodicityAndOffset, nzpcsi->periodicityAndOffset);
ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceToAddModList->list,imres);
csi_MeasConfig->csi_IM_ResourceSetToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceSetToAddModList));
NR_CSI_IM_ResourceSet_t *imset = calloc(1,sizeof(*imset));
imset->csi_IM_ResourceSetId = 0;
NR_CSI_IM_ResourceId_t *res = calloc(1,sizeof(*res));
*res = imres->csi_IM_ResourceId;
ASN_SEQUENCE_ADD(&imset->csi_IM_Resources,res);
ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceSetToAddModList->list,imset);
}
else {
csi_MeasConfig->csi_IM_ResourceToAddModList = NULL;
csi_MeasConfig->csi_IM_ResourceSetToAddModList = NULL;
}
csi_MeasConfig->csi_IM_ResourceToReleaseList = NULL;
csi_MeasConfig->csi_IM_ResourceSetToReleaseList = NULL;
}
void prepare_sim_uecap(NR_UE_NR_Capability_t *cap,
NR_ServingCellConfigCommon_t *scc,
int numerology,
......
......@@ -119,6 +119,8 @@ void config_csirs(NR_ServingCellConfigCommon_t *servingcellconfigcommon,
int num_dl_antenna_ports,
int curr_bwp,
int do_csirs);
void config_csiim(int do_csirs, int dl_antenna_ports, int curr_bwp,
NR_CSI_MeasConfig_t *csi_MeasConfig);
void set_dl_mcs_table(int scs, NR_UE_NR_Capability_t *cap,
NR_SpCellConfig_t *SpCellConfig,
NR_BWP_DownlinkDedicated_t *bwp_Dedicated,
......
......@@ -1034,39 +1034,8 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco
NR_CSI_MeasConfig_t *csi_MeasConfig = calloc(1,sizeof(*csi_MeasConfig));
secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup = csi_MeasConfig;
if (do_csirs && dl_antenna_ports > 1) {
csi_MeasConfig->csi_IM_ResourceToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceToAddModList));
NR_CSI_IM_Resource_t *imres0 = calloc(1,sizeof(*imres0));
imres0->csi_IM_ResourceId = 0;
imres0->csi_IM_ResourceElementPattern = calloc(1,sizeof(*imres0->csi_IM_ResourceElementPattern));
imres0->csi_IM_ResourceElementPattern->present = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern_PR_pattern1;
imres0->csi_IM_ResourceElementPattern->choice.pattern1 = calloc(1,sizeof(*imres0->csi_IM_ResourceElementPattern->choice.pattern1));
imres0->csi_IM_ResourceElementPattern->choice.pattern1->subcarrierLocation_p1 = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern__pattern1__subcarrierLocation_p1_s4;
imres0->csi_IM_ResourceElementPattern->choice.pattern1->symbolLocation_p1 = 6;
imres0->freqBand = calloc(1,sizeof(*imres0->freqBand));
imres0->freqBand->startingRB = 0;
imres0->freqBand->nrofRBs = ((curr_bwp>>2)+(curr_bwp%4>0))<<2;
imres0->periodicityAndOffset = calloc(1,sizeof(*imres0->periodicityAndOffset));
imres0->periodicityAndOffset->present = NR_CSI_ResourcePeriodicityAndOffset_PR_slots320;
imres0->periodicityAndOffset->choice.slots320 = 0;
ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceToAddModList->list,imres0);
csi_MeasConfig->csi_IM_ResourceSetToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceSetToAddModList));
NR_CSI_IM_ResourceSet_t *imset0 = calloc(1,sizeof(*imset0));
imset0->csi_IM_ResourceSetId = 0;
NR_CSI_IM_ResourceId_t *res0 = calloc(1,sizeof(*res0));
*res0 = 0;
ASN_SEQUENCE_ADD(&imset0->csi_IM_Resources,res0);
ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceSetToAddModList->list,imset0);
}
else {
csi_MeasConfig->csi_IM_ResourceToAddModList = NULL;
csi_MeasConfig->csi_IM_ResourceSetToAddModList = NULL;
}
csi_MeasConfig->csi_IM_ResourceToReleaseList = NULL;
csi_MeasConfig->csi_IM_ResourceSetToReleaseList = NULL;
config_csirs(servingcellconfigcommon, csi_MeasConfig, uid, dl_antenna_ports, curr_bwp, do_csirs);
config_csiim(do_csirs, dl_antenna_ports, curr_bwp, csi_MeasConfig);
csi_MeasConfig->csi_SSB_ResourceSetToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_SSB_ResourceSetToAddModList));
csi_MeasConfig->csi_SSB_ResourceSetToReleaseList = NULL;
......
......@@ -61,6 +61,8 @@ Setting the env variable RFSIMULATOR can be used instead of using the serveraddr
## How to use the RF simulator options
To define and use a channel model, the configuration file needs to include a channel configuration file. To do this, add `@include "channelmod_rfsimu.conf"` to the end of the configuration file, and place the channel configuration file in the same directory. An example channel configuration file `channelmod_rfsimu.conf` is in `ci-scripts/conf_files`.
Add the following options to the command line to enable the channel model and the IQ samples saving for future replay:
```bash
--rfsimulator.options chanmod,saviq
......@@ -82,6 +84,8 @@ Example run:
sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf --parallel-config PARALLEL_SINGLE_THREAD --rfsim --phy-test --rfsimulator.options chanmod --rfsimulator.modelname AWGN
```
where `@include "channelmod_rfsimu.conf"` has been added at the end of the file, and `ci-scripts/conf_files/channelmod_rfsimu.conf` copied to `targets/PROJECTS/GENERIC-LTE-EPC/CONF/`.
## 4G case
For the UE, it should be set to the IP address of the eNB. For example:
......@@ -102,9 +106,7 @@ $OPENAIR_DIR/targets/bin/conf2uedata -c $OPENAIR_DIR/openair3/NAS/TOOLS/ue_eurec
## 5G case
The 5G RF simulator will be aligned with the 4G as the efforts for merging the 5G specific branches into the develop make progresses.
After regular build, add the simulation driver (do not use ./build_oai -w SIMU until 4G and 5G branches are merged).
If `build_oai` has not been run with `-w SIMU`, you need to build the `rfsimulator` manually. To do so:
```bash
cd ran_build/build
make rfsimulator
......@@ -113,7 +115,7 @@ make rfsimulator
### Launch gNB in one window
```bash
sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf --parallel-config PARALLEL_SINGLE_THREAD --rfsim --phy-test
sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf --parallel-config PARALLEL_SINGLE_THREAD --rfsim --phy-test --nokrnmod 1
```
### Launch UE in another window
......@@ -124,15 +126,17 @@ sudo RFSIMULATOR=<TARGET_GNB_INTERFACE_ADDRESS> ./nr-uesoftmodem --rfsim --phy-t
Notes:
1. <TARGET_GNB_INTERFACE_ADDRESS> can be 127.0.0.1 if both gNB and nrUE executables run on the same host, OR the IP interface address of the remote host running the gNB executable, if the gNB and nrUE run on separate hosts
2. the --rrc_config_path parameter SHALL specify where the 2 RAW files are located (`rbconfig.raw` and `reconfig.raw`).
1. This starts the gNB and UE in the `phy-test` UP-only mode where the gNB is started as if a UE had already connected, and a configurable scheduler is used instead of the default one. The options `-m`, `-l`, `-t`, `-M`, `-T`, `-D`, and `-U` can be used to configure this scheduler.
2. <TARGET_GNB_INTERFACE_ADDRESS> can be 127.0.0.1 if both gNB and nrUE executables run on the same host, OR the IP interface address of the remote host running the gNB executable, if the gNB and nrUE run on separate hosts.
3. The --rrc_config_path parameter SHALL specify where the 2 RAW files are located (`rbconfig.raw` and `reconfig.raw`).
- If you are running on the same machine and launched the 2 executables (`nr-softmodem` and `nr-uesoftmodem`) from the same directory, nothing has to be done.
- If you launched the 2 executables from 2 different folders, just point to the location where you launched the `nr-softmodem`:
* `sudo RFSIMULATOR=<TARGET_GNB_INTERFACE_ADDRESS> ./nr-uesoftmodem --rfsim --phy-test --rrc_config_path /the/path/where/you/launched/nr-softmodem`
- If you are not running on the same machine or launched the 2 executables from 2 different folders, you need to **COPY** the 2 raw files
* `scp usera@machineA:/the/path/where/you/launched/nr-softmodem/r*config.raw userb@machineB:/the/path/where/you/will/launch/nr-uesoftmodem/`
* Obviously this operation SHALL be done before launching the `nr-uesoftmodem` executable.
3. to enable the noS1 mode --noS1 and --nokrnmod 1 options should be added to the command line
4. To enable the noS1 mode, `--noS1` option should be added to the command line.
5. To operate the gNB/UE with a 5GC, start them using the `--sa` option. More information can be found [here](../../../doc/TESTING_5GSA_setup.md#2-sa-setup-with-oai-nr-ue-softmodem).
In the UE, you can add `-d` option to get the softscope.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment