Merge remote-tracking branch 'origin/prs_bug_fix_and_integrate_to_ci' into integration_2025_w45 (!3670)

PRS bug fix and CI Integration

- Fix the bug in PRS channel estimation while copying memory to compute the
  impulse response
- Integrate PRS testing to CI
This commit is contained in:
Jaroslava Fiedlerova
2025-11-06 09:36:03 +01:00
12 changed files with 688 additions and 38 deletions
+2 -2
View File
@@ -233,10 +233,10 @@ def Custom_Command(HTML, node, command):
HTML.CreateHtmlTestRowQueue(command, status, message)
return status == 'OK' or status == 'Warning'
def Custom_Script(HTML, node, script):
def Custom_Script(HTML, node, script, args):
logging.info(f"Executing custom script on {node}")
with cls_cmd.getConnection(node) as c:
ret = c.exec_script(script, 90)
ret = c.exec_script(script, 90, args)
logging.debug(f"Custom_Script: {script} on node: {node} - return code {ret.returncode}, output:\n{ret.stdout}")
status = 'OK'
message = [ret.stdout]
@@ -0,0 +1,256 @@
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({mcc = 208; mnc = 97; mnc_length = 2;});
////////// Physical parameters:
min_rxtxtime = 6;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3300.60 MHz + 53*12*30e-3 MHz = 3319.68
absoluteFrequencySSB = 621312;
# this is 3300.60 MHz
dl_absoluteFrequencyPointA = 620040;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
# this is RBstart=0,L=106 (275*(L-1))+RBstart
initialDLBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 11;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 98;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 12;
preambleReceivedTargetPower = -104;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#one (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 1;
p0_NominalWithGrant =-90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -90;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spar e1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -25;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "CI_MME_IP_ADDR"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "CI_GNB_IP_ADDR";
GNB_IPV4_ADDRESS_FOR_NGU = "CI_GNB_IP_ADDR";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 200;
}
);
prs_config = (
{
NumPRSResources = 1;
PRSResourceSetPeriod = [20, 2];
SymbolStart = [7];
NumPRSSymbols = [6];
NumRB = 106;
RBOffset = 0;
CombSize = 4;
REOffset = [0];
PRSResourceOffset = [0];
PRSResourceRepetition = 1;
PRSResourceTimeGap = 1;
NPRS_ID = [0];
MutingPattern1 = [];
MutingPattern2 = [];
MutingBitRepetition = 1;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 120;
}
);
RUs = (
{
local_rf = "yes"
nb_tx = 1
nb_rx = 1
att_tx = 0
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
}
);
rfsimulator :
{
serveraddr = "server";
serverport = 4043;
options = (); #("saviq"); or/and "chanmod"
modelname = "AWGN";
IQfile = "/tmp/rfsimulator.iqs";
};
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level = "info";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
f1ap_log_level = "debug";
};
@@ -0,0 +1,89 @@
PRSs =
(
{
Active_gNBs = 1;
prs_config0 = (
{
gNB_id = 0;
NumPRSResources = 1;
PRSResourceSetPeriod = [20, 2];
SymbolStart = [7];
NumPRSSymbols = [6];
NumRB = 106;
RBOffset = 0;
CombSize = 4;
REOffset = [0];
PRSResourceOffset = [0];
PRSResourceRepetition = 1;
PRSResourceTimeGap = 1;
NPRS_ID = [0];
MutingPattern1 = [];
MutingPattern2 = [];
MutingBitRepetition = 1;
}
);
prs_config1 = (
{
gNB_id = 1;
NumPRSResources = 1;
PRSResourceSetPeriod = [20, 2];
SymbolStart = [7];
NumPRSSymbols = [6];
NumRB = 106;
RBOffset = 0;
CombSize = 4;
REOffset = [0];
PRSResourceOffset = [1];
PRSResourceRepetition = 1;
PRSResourceTimeGap = 1;
NPRS_ID = [1];
MutingPattern1 = [];
MutingPattern2 = [];
MutingBitRepetition = 1;
}
);
prs_config2 = (
{
gNB_id = 2;
NumPRSResources = 1;
PRSResourceSetPeriod = [20, 2];
SymbolStart = [7];
NumPRSSymbols = [6];
NumRB = 106;
RBOffset = 0;
CombSize = 4;
REOffset = [0];
PRSResourceOffset = [2];
PRSResourceRepetition = 1;
PRSResourceTimeGap = 1;
NPRS_ID = [2];
MutingPattern1 = [];
MutingPattern2 = [];
MutingBitRepetition = 1;
}
);
prs_config3 = (
{
gNB_id = 3;
NumPRSResources = 1;
PRSResourceSetPeriod = [20, 2];
SymbolStart = [7];
NumPRSSymbols = [6];
NumRB = 106;
RBOffset = 0;
CombSize = 4;
REOffset = [0];
PRSResourceOffset = [3];
PRSResourceRepetition = 1;
PRSResourceTimeGap = 1;
NPRS_ID = [3];
MutingPattern1 = [];
MutingPattern2 = [];
MutingBitRepetition = 1;
}
);
}
);
+2 -1
View File
@@ -268,9 +268,10 @@ def ExecuteActionWithParam(action, ctx, node):
elif action == 'Custom_Script':
script = test.findtext('script')
args = test.findtext('args')
# Allow referencing repository workspace path in XML via %%workspace%%
script = script.replace("%%workspace%%", CONTAINERS.eNBSourceCodePath)
success = cls_oaicitest.Custom_Script(HTML, node, script)
success = cls_oaicitest.Custom_Script(HTML, node, script, args)
elif action == 'Pull_Cluster_Image':
tag_prefix = test.findtext('tag_prefix') or ""
+89
View File
@@ -0,0 +1,89 @@
#!/bin/bash
[[ $# -lt 1 ]] && { echo "Usage: $0 <distance_in_meters...>"; exit 1; }
DISTANCES=("$@")
IP=192.168.71.150
PORT=8091
NCAT_TIMEOUT=1 #s
SLEEP_WAIT=4 #s
MAX_RETRIES=3
set_and_verify_distance() {
local distance=$1
echo "Testing PRS ToA estimation for distance: $distance m"
# it seems that grep returns immediately with this syntax, but not echo | ncat | grep
# so prefer this to receive new distance immediately. We use --idle to keep
# ncat open for some additional time
local setdist_resp="$(grep --max-count 1 new_offset <(echo rfsimu setdistance rfsimu_channel_enB0 $distance | ncat --idle ${NCAT_TIMEOUT} ${IP} ${PORT}))"
echo "> response: ${setdist_resp}"
local gettoa_resp="$(echo "ciUE get_max_dl_toa" | ncat ${IP} ${PORT} | grep "UE max PRS DL ToA")"
echo "> response: ${gettoa_resp}"
[[ -z "$setdist_resp" || -z "$gettoa_resp" ]] && return 1
# Extract ToA values
[[ "$setdist_resp" =~ new_offset\ ([0-9]+) ]] && local set_toa="${BASH_REMATCH[1]}" || { echo "> Set ToA extraction failed for distance: $distance m"; return 1; }
[[ "$gettoa_resp" =~ UE\ max\ PRS\ DL\ ToA\ ([0-9]+) ]] && local est_toa="${BASH_REMATCH[1]}" || { echo "> Estimated ToA extraction failed for distance: $distance m"; return 1; }
# Compare extracted ToA values
[[ $set_toa == $est_toa ]] && echo "PRS SUCCESS for distance: $distance m" || { echo "PRS FAILURE for distance: $distance m (Actual ToA=$set_toa, Estimated ToA=$est_toa)" ; return 1; }
}
test_distance() {
local distance=$1 retries=0
# retry loop incase we didn't receive the response
while (( retries < MAX_RETRIES )); do
echo " Attempt $((retries + 1))/$MAX_RETRIES"
# Always reset to 0 m before testing target distance
if ! set_and_verify_distance 0; then
echo " Set distance 0 m failed during attempt $((retries + 1))"
else
sleep "$SLEEP_WAIT"
# Now test the actual target distance
if set_and_verify_distance "$distance"; then
return 0
fi
fi
((retries++))
if (( retries < MAX_RETRIES )); then
sleep "$SLEEP_WAIT"
else
echo " ERROR: No valid response after $MAX_RETRIES retries for distance: $distance m"
return 1
fi
done
}
num_success=0
num_fail=0
for d in "${DISTANCES[@]}"; do
if test_distance "$d"; then
((num_success++))
else
((num_fail++))
fi
sleep "$SLEEP_WAIT"
done
# ---- Summary ----
echo
echo "==================== SUMMARY ===================="
echo "Total tests run : ${#DISTANCES[@]}"
echo "Successful tests: ${num_success}"
echo "Failed tests : ${num_fail}"
echo "================================================="
if [ $num_success -gt 0 ]; then
exit 0
else
exit 1
fi
@@ -0,0 +1,100 @@
<!--
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>rfsim-5gnr-prs</htmlTabRef>
<htmlTabName>Monolithic gNB with PRS</htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<TestCaseRequestedList>
111111
800813
000012
000013
040001
040023
100011
222222
</TestCaseRequestedList>
<TestCaseExclusionList></TestCaseExclusionList>
<testCase id="111111">
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<node>localhost</node>
<images>oai-gnb-asan oai-nr-ue-asan</images>
</testCase>
<testCase id="800813">
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<node>localhost</node>
</testCase>
<testCase id="000012">
<class>Deploy_Object</class>
<desc>Deploy OAI 5G gNB+UE RF sim SA</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_prs</yaml_path>
<services>oai-gnb oai-nr-ue</services>
</testCase>
<testCase id="000013">
<class>Attach_UE</class>
<desc>Attach OAI UE (Wait for IP)</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
</testCase>
<testCase id="040001">
<class>IdleSleep</class>
<desc>Sleep</desc>
<idle_sleep_time_in_sec>2</idle_sleep_time_in_sec>
</testCase>
<testCase id="040023">
<class>Custom_Script</class>
<always_exec>true</always_exec>
<desc>Set and verify distance at 50, 100, 150 m</desc>
<node>localhost</node>
<script>scripts/set-and-verify-distance-prs.sh</script>
<args>50 100 150</args>
</testCase>
<testCase id="100011">
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy all OAI 5G stack</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_prs</yaml_path>
<d_retx_th>30,100,100,100</d_retx_th>
<u_retx_th>30,100,100,100</u_retx_th>
</testCase>
<testCase id="222222">
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<node>localhost</node>
<images>oai-gnb-asan oai-nr-ue-asan</images>
</testCase>
</testCaseList>
@@ -0,0 +1,71 @@
services:
oai-gnb:
image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop}
container_name: rfsim5g-oai-gnb
cap_drop:
- ALL
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
environment:
USE_ADDITIONAL_OPTIONS: --phy-test --rfsim -E
--log_config.global_log_options level,nocolor,time
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
networks:
public_net:
ipv4_address: 192.168.71.140
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ../../conf_files/gnb.sa.band78.106prb.rfsim.prs.conf:/opt/oai-gnb/etc/gnb.conf
- rrc.config:/opt/oai-gnb/
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
interval: 10s
timeout: 5s
retries: 5
oai-nr-ue:
image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
container_name: rfsim5g-oai-nr-ue
cap_drop:
- ALL
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
environment:
USE_ADDITIONAL_OPTIONS: --phy-test --rfsim -E
--reconfig-file etc/rrc/reconfig.raw --rbconfig-file etc/rrc/rbconfig.raw
--rfsimulator.serveraddr 192.168.71.140
--log_config.global_log_options level,nocolor,time
--telnetsrv --telnetsrv.listenaddr 192.168.71.150 --telnetsrv.listenport 8091
--telnetsrv.shrmod ciUE
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ../../conf_files/nrue.band78.106prb.prs.conf:/opt/oai-nr-ue/etc/nr-ue.conf
- rrc.config:/opt/oai-nr-ue/etc/rrc/
depends_on:
- oai-gnb
networks:
public_net:
ipv4_address: 192.168.71.150
healthcheck:
test: /bin/bash -c "pgrep nr-uesoftmodem"
interval: 10s
timeout: 5s
retries: 5
volumes:
rrc.config:
networks:
public_net:
driver: bridge
name: rfsim5g-oai-public-net
ipam:
config:
- subnet: 192.168.71.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-public"
+23
View File
@@ -37,6 +37,8 @@
#include "openair2/LAYER2/NR_MAC_UE/mac_defs.h"
#include "openair2/LAYER2/NR_MAC_UE/mac_proto.h"
#include "openair2/RRC/NR_UE/rrc_proto.h"
#include "openair1/PHY/phy_extern_nr_ue.h"
#include "openair1/PHY/defs_nr_common.h"
#define TELNETSERVERCODE
#include "telnetsrv.h"
@@ -110,6 +112,26 @@ static int force_deregistration(char *buf, int debug, telnet_printfunc_t prnt)
return 0;
}
extern float get_prs_max_dl_toa(prs_meas_t *prs_meas);
static int get_dl_toa(char *buf, int debug, telnet_printfunc_t prnt)
{
// TODO multiple antennas, resources, gNBs?
int gNB_id = 0;
int rsc_id = 0;
int ant = 0;
PHY_VARS_NR_UE *UE = PHY_vars_UE_g[0][0];
if (!UE || !UE->prs_vars[gNB_id])
ERROR_MSG_RET("no UE/prs_vars found!\n");
NR_PRS_RESOURCE_t *prs_res = &UE->prs_vars[gNB_id]->prs_resource[rsc_id];
if (!prs_res->prs_meas || !prs_res->prs_meas[ant])
ERROR_MSG_RET("prs_meas not initialized!\n");
float max = get_prs_max_dl_toa(prs_res->prs_meas[ant]);
prnt("UE max PRS DL ToA %.3f\n", max);
return 0;
}
/* Telnet shell command definitions */
static telnetshell_cmddef_t cicmds[] = {
{"sync_state", "[UE_ID(int,opt)]", get_sync_state},
@@ -117,6 +139,7 @@ static telnetshell_cmddef_t cicmds[] = {
{"force_RRC_IDLE", "", force_RRC_IDLE},
{"force_crnti_ra", "", force_crnti_ra},
{"deregistration", "", force_deregistration},
{"get_max_dl_toa", "[ant]", get_dl_toa},
{"", "", NULL},
};
+3
View File
@@ -157,6 +157,9 @@ void init_nr_prs_ue_vars(PHY_VARS_NR_UE *ue)
for (int j=0; j<fp->nb_antennas_rx; j++) {
prs_vars[idx]->prs_resource[k].prs_meas[j] = malloc16_clear(sizeof(prs_meas_t));
AssertFatal((prs_vars[idx]->prs_resource[k].prs_meas[j]!=NULL), "%s: PRS measurements malloc failed for gNB_id %d, rx_ant %d\n", __FUNCTION__, idx, j);
prs_meas_t *m = prs_vars[idx]->prs_resource[k].prs_meas[j];
m->next_dl_toa = m->dl_toa;
pthread_mutex_init(&m->dl_toa_mtx, NULL);
}
}
}
@@ -47,12 +47,12 @@
#define NO_INTERP 1
/* Generic function to find the peak of channel estimation buffer */
void peak_estimator(int32_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t *peak_val, int32_t mean_val)
void peak_estimator(c16_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t *peak_val, int32_t mean_val)
{
int32_t max_val = 0, max_idx = 0, abs_val = 0;
for(int k = 0; k < buf_len; k++)
{
abs_val = squaredMod(((c16_t*)buffer)[k]);
abs_val = squaredMod(buffer[k]);
if(abs_val > max_val)
{
max_val = abs_val;
@@ -71,6 +71,32 @@ void peak_estimator(int32_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t
}
}
void set_prs_dl_toa(prs_meas_t *prs_meas, float dl_toa)
{
int rc = pthread_mutex_lock(&prs_meas->dl_toa_mtx);
AssertFatal(rc == 0, "pthread_mutex_lock() failed: errno %d, %s\n", errno, strerror(errno));
*prs_meas->next_dl_toa++ = dl_toa;
if (prs_meas->next_dl_toa >= prs_meas->dl_toa + sizeofArray(prs_meas->dl_toa))
prs_meas->next_dl_toa = prs_meas->dl_toa;
LOG_D(NR_PHY, "next_dl_toa %p\n", prs_meas->next_dl_toa);
rc = pthread_mutex_unlock(&prs_meas->dl_toa_mtx);
AssertFatal(rc == 0, "pthread_mutex_unlock() failed: errno %d, %s\n", errno, strerror(errno));
}
float get_prs_max_dl_toa(prs_meas_t *prs_meas)
{
float max = 0.0f;
int rc = pthread_mutex_lock(&prs_meas->dl_toa_mtx);
AssertFatal(rc == 0, "pthread_mutex_lock() failed: errno %d, %s\n", errno, strerror(errno));
for (int i = 0; i < sizeofArray(prs_meas->dl_toa); ++i) {
max = cmax(max, prs_meas->dl_toa[i]);
LOG_I(NR_PHY, "i %d dl_toa %.3f max %.3f\n", i, prs_meas->dl_toa[i], max);
}
rc = pthread_mutex_unlock(&prs_meas->dl_toa_mtx);
AssertFatal(rc == 0, "pthread_mutex_unlock() failed: errno %d, %s\n", errno, strerror(errno));
return max;
}
int nr_prs_channel_estimation(uint8_t gNB_id,
uint8_t rsc_id,
uint8_t rep_num,
@@ -83,8 +109,8 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
prs_config_t *prs_cfg = &ue->prs_vars[gNB_id]->prs_resource[rsc_id].prs_cfg;
prs_meas_t **prs_meas = ue->prs_vars[gNB_id]->prs_resource[rsc_id].prs_meas;
c16_t ch_tmp_buf[ ue->frame_parms.ofdm_symbol_size] __attribute__((aligned(32)));
int32_t chF_interpol[frame_params->nb_antennas_rx][NR_PRS_IDFT_OVERSAMP_FACTOR*ue->frame_parms.ofdm_symbol_size] __attribute__((aligned(32)));
int32_t chT_interpol[frame_params->nb_antennas_rx][NR_PRS_IDFT_OVERSAMP_FACTOR*ue->frame_parms.ofdm_symbol_size] __attribute__((aligned(32)));
c16_t chF_interpol[frame_params->nb_antennas_rx][NR_PRS_IDFT_OVERSAMP_FACTOR*ue->frame_parms.ofdm_symbol_size] __attribute__((aligned(32)));
c16_t chT_interpol[frame_params->nb_antennas_rx][NR_PRS_IDFT_OVERSAMP_FACTOR*ue->frame_parms.ofdm_symbol_size] __attribute__((aligned(32)));
memset(ch_tmp_buf,0,sizeof(ch_tmp_buf));
memset(chF_interpol,0,sizeof(chF_interpol));
memset(chT_interpol,0,sizeof(chF_interpol));
@@ -315,10 +341,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
//Start pilot
c16_t ch = c16MulConjShift(*pil, *rxF, 15);
multadd_real_vector_complex_scalar(fl,
ch,
ch_tmp,
16);
multadd_real_vector_complex_scalar(fl, ch, ch_tmp, 16);
// SNR & RSRP estimation
rsrp += squaredMod(*rxF);
@@ -331,10 +354,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
k = (k+prs_cfg->CombSize) % frame_params->ofdm_symbol_size;
rxF = &rxdataF[rxAnt][l * frame_params->ofdm_symbol_size + k];
ch = c16MulConjShift(*pil, *rxF, 15);
multadd_real_vector_complex_scalar(fml,
ch,
ch_tmp,
16);
multadd_real_vector_complex_scalar(fml, ch, ch_tmp, 16);
// SNR & RSRP estimation
rsrp += squaredMod(*rxF);
@@ -352,10 +372,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
for(int pIdx = 2; pIdx < num_pilots-2; pIdx++)
{
c16_t ch = c16MulConjShift(*pil, *rxF, 15);
multadd_real_vector_complex_scalar(fmm,
ch,
ch_tmp,
16);
multadd_real_vector_complex_scalar(fmm, ch, ch_tmp, 16);
// SNR & RSRP estimation
rsrp += squaredMod(*rxF);
@@ -372,10 +389,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
//End pilot
ch = c16MulConjShift(*pil, *rxF, 15);
multadd_real_vector_complex_scalar(fmr,
ch,
ch_tmp,
16);
multadd_real_vector_complex_scalar(fmr, ch, ch_tmp, 16);
// SNR & RSRP estimation
rsrp += squaredMod(*rxF);
@@ -388,10 +402,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
k = (k+prs_cfg->CombSize) % frame_params->ofdm_symbol_size;
rxF = &rxdataF[rxAnt][l * frame_params->ofdm_symbol_size + k];
ch = c16MulConjShift(*pil, *rxF, 15);
multadd_real_vector_complex_scalar(fr,
ch,
ch_tmp,
16);
multadd_real_vector_complex_scalar(fr, ch, ch_tmp, 16);
// SNR & RSRP estimation
rsrp += squaredMod(*rxF);
@@ -435,9 +446,9 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
// Place PRS channel estimates in FFT shifted format
if(first_half > 0)
memcpy((int16_t *)&chF_interpol[rxAnt][start_offset], &ch_tmp[0], first_half * sizeof(int32_t));
memcpy(&chF_interpol[rxAnt][start_offset], &ch_tmp[0], first_half * sizeof(c16_t));
if(second_half > 0)
memcpy((int16_t *)&chF_interpol[rxAnt][0], &ch_tmp[first_half << 1], second_half * sizeof(int32_t));
memcpy(&chF_interpol[rxAnt][0], &ch_tmp[first_half], second_half * sizeof(c16_t));
// Convert to time domain
freq2time(NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size,
@@ -463,9 +474,9 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
prs_meas[rxAnt]->slot = proc->nr_slot_rx;
prs_meas[rxAnt]->rxAnt_idx = rxAnt;
prs_meas[rxAnt]->dl_aoa = rsc_id;
prs_meas[rxAnt]->dl_toa = prs_toa / (float)NR_PRS_IDFT_OVERSAMP_FACTOR;
if ((frame_params->ofdm_symbol_size - prs_meas[rxAnt]->dl_toa) < frame_params->ofdm_symbol_size / 2)
prs_meas[rxAnt]->dl_toa -= (frame_params->ofdm_symbol_size);
float dl_toa = prs_toa / (float)NR_PRS_IDFT_OVERSAMP_FACTOR;
if ((frame_params->ofdm_symbol_size - dl_toa) < frame_params->ofdm_symbol_size / 2)
dl_toa -= (frame_params->ofdm_symbol_size);
LOG_I(PHY,
"[gNB %d][rsc %d][Rx %d][sfn %d][slot %d] DL PRS ToA ==> %.1f / %d samples, peak channel power %.1f dBm, SNR %+.1f dB, rsrp %+.1f dBm\n",
gNB_id,
@@ -473,12 +484,14 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
rxAnt,
proc->frame_rx,
proc->nr_slot_rx,
prs_meas[rxAnt]->dl_toa,
dl_toa,
frame_params->ofdm_symbol_size,
ch_pwr_dbm,
prs_meas[rxAnt]->snr,
prs_meas[rxAnt]->rsrp_dBm);
set_prs_dl_toa(prs_meas[rxAnt], dl_toa);
#ifdef DEBUG_PRS_CHEST
sprintf(filename, "%s%i%s", "PRSpilot_", rxAnt, ".m");
LOG_M(filename, "prs_loc", mod_prs, num_pilots, 1, 1);
@@ -496,7 +509,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
// T tracer dump
T(T_UE_PHY_INPUT_SIGNAL, T_INT(gNB_id),
T_INT(proc->frame_rx), T_INT(proc->nr_slot_rx),
T_INT(rxAnt), T_BUFFER(&rxdataF[rxAnt][0], frame_params->samples_per_slot_wCP*sizeof(int32_t)));
T_INT(rxAnt), T_BUFFER(&rxdataF[rxAnt][0], frame_params->samples_per_slot_wCP*sizeof(c16_t)));
T(T_UE_PHY_DL_CHANNEL_ESTIMATE_FREQ,
T_INT(gNB_id),
@@ -504,7 +517,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
T_INT(proc->frame_rx),
T_INT(proc->nr_slot_rx),
T_INT(rxAnt),
T_BUFFER(&chF_interpol[rxAnt][0], NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size * sizeof(int32_t)));
T_BUFFER(&chF_interpol[rxAnt][0], NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size * sizeof(c16_t)));
T(T_UE_PHY_DL_CHANNEL_ESTIMATE,
T_INT(gNB_id),
@@ -512,7 +525,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
T_INT(proc->frame_rx),
T_INT(proc->nr_slot_rx),
T_INT(rxAnt),
T_BUFFER(&chT_interpol[rxAnt][0], NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size * sizeof(int32_t)));
T_BUFFER(&chT_interpol[rxAnt][0], NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size * sizeof(c16_t)));
}
return(0);
@@ -39,7 +39,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id,
c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]);
/* Generic function to find the peak of channel estimation buffer */
void peak_estimator(int32_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t *peak_val, int32_t mean_val);
void peak_estimator(c16_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t *peak_val, int32_t mean_val);
/*!
\brief This function performs channel estimation including frequency and temporal interpolation
+6 -1
View File
@@ -38,6 +38,8 @@
#include "impl_defs_nr.h"
#include "PHY/CODING/nrPolar_tools/nr_polar_defs.h"
#include <pthread.h>
#define MAX_NUM_SUBCARRIER_SPACING 5
#define NR_MAX_OFDM_SYMBOL_SIZE 8192
@@ -312,7 +314,10 @@ typedef struct {
int32_t sfn;
int8_t slot;
int8_t rxAnt_idx;
float dl_toa;
pthread_mutex_t dl_toa_mtx; // protect reading of max from write
// circular buffer to be able to read maximum of last estimations
float dl_toa[128]; // set through set_prs_dl_toa()
float *next_dl_toa;
int32_t dl_aoa;
float snr;
float rsrp;