diff --git a/CMakeLists.txt b/CMakeLists.txt
index 30721256a8..22453e5af6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -717,6 +717,7 @@ target_link_libraries(SCHED_UE_LIB PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs)
set(SCHED_SRC_NR_UE
${OPENAIR1_DIR}/SCHED_NR_UE/phy_procedures_nr_ue.c
+ ${OPENAIR1_DIR}/SCHED_NR_UE/phy_procedures_nr_ue_sl.c
${OPENAIR1_DIR}/SCHED_NR_UE/fapi_nr_ue_l1.c
${OPENAIR1_DIR}/SCHED_NR_UE/phy_frame_config_nr_ue.c
${OPENAIR1_DIR}/SCHED_NR_UE/harq_nr.c
@@ -1084,8 +1085,11 @@ set(PHY_SRC_UE
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/sss_nr.c
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/cic_filter_nr.c
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_initial_sync.c
+ ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_initial_sync_sl.c
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_ue_rf_helpers.c
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_pbch.c
+ ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c
+ ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_psbch_tx.c
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c
${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
@@ -2237,6 +2241,22 @@ target_link_libraries(nr_pbchsim PRIVATE
)
target_link_libraries(nr_pbchsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
+add_executable(nr_psbchsim
+ ${OPENAIR1_DIR}/SIMULATION/NR_PHY/psbchsim.c
+ ${OPENAIR1_DIR}/SIMULATION/NR_PHY/nr_dummy_functions.c
+ ${OPENAIR_DIR}/common/utils/nr/nr_common.c
+ ${OPENAIR_DIR}/executables/softmodem-common.c
+ ${OPENAIR2_DIR}/RRC/NAS/nas_config.c
+ ${NR_UE_RRC_DIR}/rrc_nsa.c
+ ${NFAPI_USER_DIR}/nfapi.c
+ ${NFAPI_USER_DIR}/gnb_ind_vars.c
+ ${PHY_INTERFACE_DIR}/queue_t.c
+ )
+target_link_libraries(nr_psbchsim PRIVATE
+ -Wl,--start-group UTIL SIMU SIMU_ETH PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -lz -Wl,--end-group
+ m pthread ${T_LIB} ITTI dl shlib_loader
+ )
+target_link_libraries(nr_psbchsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
#PUCCH ---> Prashanth
add_executable(nr_pucchsim
diff --git a/charts/physims/charts/nr-psbchsim/Chart.yaml b/charts/physims/charts/nr-psbchsim/Chart.yaml
new file mode 100644
index 0000000000..8f1af6d648
--- /dev/null
+++ b/charts/physims/charts/nr-psbchsim/Chart.yaml
@@ -0,0 +1,36 @@
+apiVersion: v1
+name: oai-nr-psbchsim
+description: A Helm subchart for nr-psbchsim network function
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+version: 0.1.1
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application.
+appVersion: v1
+
+keywords:
+ - Physical Simulator
+ - nr-psbchsim
+ - RAN
+ - 5G
+
+sources:
+ - https://gitlab.eurecom.fr/oai/openairinterface5g
+
+maintainers:
+ - name: OPENAIRINTERFACE
+ email: contact@openairinterface.org
diff --git a/charts/physims/charts/nr-psbchsim/templates/_helpers.tpl b/charts/physims/charts/nr-psbchsim/templates/_helpers.tpl
new file mode 100644
index 0000000000..ef7fc150b5
--- /dev/null
+++ b/charts/physims/charts/nr-psbchsim/templates/_helpers.tpl
@@ -0,0 +1,63 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "oai-nr-psbchsim.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "oai-nr-psbchsim.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "oai-nr-psbchsim.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Common labels
+*/}}
+{{- define "oai-nr-psbchsim.labels" -}}
+helm.sh/chart: {{ include "oai-nr-psbchsim.chart" . }}
+{{ include "oai-nr-psbchsim.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end -}}
+
+{{/*
+Selector labels
+*/}}
+{{- define "oai-nr-psbchsim.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "oai-nr-psbchsim.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end -}}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "oai-nr-psbchsim.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create -}}
+ {{ default (include "oai-nr-psbchsim.fullname" .) .Values.serviceAccount.name }}
+{{- else -}}
+ {{ default "default" .Values.serviceAccount.name }}
+{{- end -}}
+{{- end -}}
diff --git a/charts/physims/charts/nr-psbchsim/templates/job.yaml b/charts/physims/charts/nr-psbchsim/templates/job.yaml
new file mode 100644
index 0000000000..a302630d3b
--- /dev/null
+++ b/charts/physims/charts/nr-psbchsim/templates/job.yaml
@@ -0,0 +1,42 @@
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: {{ .Chart.Name }}
+spec:
+ template:
+ metadata:
+ labels:
+ app: physim
+ spec:
+ securityContext:
+ {{- toYaml .Values.podSecurityContext | nindent 8 }}
+ {{- if .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ containers:
+ - name: physim
+ image: "{{ .Values.global.image.repository }}:{{ .Values.global.image.version }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ securityContext:
+ {{- toYaml .Values.securityContext | nindent 12 }}
+ env:
+ - name: OPENAIR_DIR
+ value: /opt/oai-physim
+ command: ["/bin/sh", "-c"]
+ args:
+ - >
+ cmake_targets/autotests/run_exec_autotests.bash -g "nr_psbchsim" -d bin/ &&
+ echo "FINISHED" && sleep infinity
+ dnsPolicy: ClusterFirst
+ restartPolicy: Never
+ schedulerName: default-scheduler
+ serviceAccountName: {{ .Values.global.serviceAccountName }}
+ terminationGracePeriodSeconds: 30
+ {{- if .Values.global.nodeSelector}}
+ nodeSelector:
+ {{- toYaml .Values.global.nodeSelector | nindent 12 }}
+ {{- end }}
+ {{- if .Values.global.nodeName.nrpsbchsim}}
+ nodeName: {{ .Values.global.nodeName.nrpsbchsim }}
+ {{- end }}
diff --git a/charts/physims/charts/nr-psbchsim/values.yaml b/charts/physims/charts/nr-psbchsim/values.yaml
new file mode 100644
index 0000000000..b6fa992ccf
--- /dev/null
+++ b/charts/physims/charts/nr-psbchsim/values.yaml
@@ -0,0 +1,48 @@
+# Default values for oai-nr-psbchsim
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+
+image:
+ # pullPolicy: IfNotPresent or Never or Always
+ pullPolicy: Always
+
+imagePullSecrets: []
+
+nameOverride: ""
+fullnameOverride: ""
+
+serviceAccount:
+ # Specifies whether a service account should be created
+ create: true
+ # Annotations to add to the service account
+ annotations: {}
+ # The name of the service account to use.
+ # If not set and create is true, a name is generated using the fullname template
+ name: "oai-nr-psbchsim"
+
+podSecurityContext:
+ runAsUser: 0
+ runAsGroup: 0
+
+securityContext:
+ privileged: false
+
+resources: {}
+ # We usually recommend not to specify default resources and to leave this as a conscious
+ # choice for the user. This also increases chances charts run on environments with little
+ # resources, such as Minikube. If you do want to specify resources, uncomment the following
+ # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
diff --git a/charts/physims/values.yaml b/charts/physims/values.yaml
index bac4ed3eb5..82374001a2 100644
--- a/charts/physims/values.yaml
+++ b/charts/physims/values.yaml
@@ -27,6 +27,7 @@ global:
nrpbschsim106rb: dedale
nrpbchsim217rb: dedale
nrpbchsim273rb: dedale
+ nrpsbchsim: dedale
nrprachsim: dedale
nrpucchsim: dedale
nrulschsim: theseus
diff --git a/ci-scripts/cls_physim1.py b/ci-scripts/cls_physim1.py
index 70f8729764..85d8520a5f 100644
--- a/ci-scripts/cls_physim1.py
+++ b/ci-scripts/cls_physim1.py
@@ -166,7 +166,7 @@ class PhySim:
mySSH.command('oc get pods -o wide -l app=physim | tee -a cmake_targets/log/physim_pods_summary.txt', '\$', 30, resync=True)
running_count = mySSH.getBefore().count('Running')
completed_count = mySSH.getBefore().count('Completed')
- if (running_count + completed_count) == 21:
+ if (running_count + completed_count) == 22:
logging.debug('\u001B[1m Running the physim test Scenarios\u001B[0m')
isRunning = True
podNames = re.findall('oai-[\S\d\w]+', mySSH.getBefore())
diff --git a/cmake_targets/autotests/test_case_list.xml b/cmake_targets/autotests/test_case_list.xml
index 81b4f02465..856b39c9fd 100755
--- a/cmake_targets/autotests/test_case_list.xml
+++ b/cmake_targets/autotests/test_case_list.xml
@@ -529,4 +529,16 @@
segmentation fault|assertion|exiting|fatal
3
+
+
+ NR-Sidelink PSBCH test cases. (Test1: SLSS Search),
+ (Test2: PSBCH TxRx)
+ nr_psbchsim
+ -I
+ -n 10
+ test1 test2
+ PSBCH test OK
+ segmentation fault|assertion|exiting|fatal
+ 3
+
diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai
index c7abef6604..bb61d356f4 100755
--- a/cmake_targets/build_oai
+++ b/cmake_targets/build_oai
@@ -324,7 +324,7 @@ function main() {
-P | --phy_simulators)
SIMUS_PHY=1
# TODO: fix: dlsim_tm4 pucchsim prachsim pdcchsim pbchsim mbmssim
- TARGET_LIST="$TARGET_LIST dlsim ulsim ldpctest polartest smallblocktest nr_pbchsim nr_dlschsim nr_ulschsim nr_dlsim nr_ulsim nr_pucchsim nr_prachsim"
+ TARGET_LIST="$TARGET_LIST dlsim ulsim ldpctest polartest smallblocktest nr_pbchsim nr_dlschsim nr_ulschsim nr_dlsim nr_ulsim nr_pucchsim nr_prachsim nr_psbchsim"
echo_info "Will compile dlsim, ulsim, ..."
shift;;
-s | --check)
diff --git a/docker/Dockerfile.phySim.rhel9 b/docker/Dockerfile.phySim.rhel9
index 9935d61979..a5a4863a64 100644
--- a/docker/Dockerfile.phySim.rhel9
+++ b/docker/Dockerfile.phySim.rhel9
@@ -69,6 +69,7 @@ COPY --from=phy-sim-build \
/oai-ran/cmake_targets/ran_build/build/ldpctest \
/oai-ran/cmake_targets/ran_build/build/nr_dlschsim \
/oai-ran/cmake_targets/ran_build/build/nr_pbchsim \
+ /oai-ran/cmake_targets/ran_build/build/nr_psbchsim \
/oai-ran/cmake_targets/ran_build/build/nr_pucchsim \
/oai-ran/cmake_targets/ran_build/build/nr_ulsim \
/oai-ran/cmake_targets/ran_build/build/smallblocktest \
diff --git a/openair1/PHY/CODING/nrPolar_tools/nr_polar_psbch_defs.h b/openair1/PHY/CODING/nrPolar_tools/nr_polar_psbch_defs.h
new file mode 100644
index 0000000000..1398c64971
--- /dev/null
+++ b/openair1/PHY/CODING/nrPolar_tools/nr_polar_psbch_defs.h
@@ -0,0 +1,57 @@
+/*
+ * 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
+ */
+
+/*! \file /PHY/CODING/nrPolar_tools/nr_polar_psbch_defs.h
+ \brief Polar definitions required for Sidelink PSBCH
+ \author
+ \date
+ \version
+ \company: Fraunhofer
+ \email:
+ \note
+ \warning
+*/
+
+#ifndef __NR_POLAR_PSBCH_DEFS__H__
+#define __NR_POLAR_PSBCH_DEFS__H__
+
+// PSBCH related polar parameters.
+
+// PSBCH symbols sent in 11RBS, 9 symbols. 11*9*(12-3(for DMRS))*2bits = 1782 bits
+#define SL_NR_POLAR_PSBCH_E_NORMAL_CP 1782
+// PSBCH symbols sent in 11RBS, 7 symbols. 11*7*(12-3(for DMRS))*2bits = 1386 bits
+#define SL_NR_POLAR_PSBCH_E_EXT_CP 1386
+// SL_NR_POLAR_PSBCH_E_NORMAL_CP/32
+#define SL_NR_POLAR_PSBCH_E_DWORD 56
+
+#define SL_NR_POLAR_PSBCH_MESSAGE_TYPE (NR_POLAR_UCI_PUCCH_MESSAGE_TYPE + 1)
+#define SL_NR_POLAR_PSBCH_PAYLOAD_BITS 32
+#define SL_NR_POLAR_PSBCH_AGGREGATION_LEVEL 0
+#define SL_NR_POLAR_PSBCH_N_MAX 9
+#define SL_NR_POLAR_PSBCH_I_IL 1
+#define SL_NR_POLAR_PSBCH_I_SEG 0
+#define SL_NR_POLAR_PSBCH_N_PC 0
+#define SL_NR_POLAR_PSBCH_N_PC_WM 0
+#define SL_NR_POLAR_PSBCH_I_BIL 0
+#define SL_NR_POLAR_PSBCH_CRC_PARITY_BITS 24
+#define SL_NR_POLAR_PSBCH_CRC_ERROR_CORRECTION_BITS 3
+
+#endif
diff --git a/openair1/PHY/CODING/nr_polar_init.c b/openair1/PHY/CODING/nr_polar_init.c
index b3e29322b3..68a3c0ffa5 100644
--- a/openair1/PHY/CODING/nr_polar_init.c
+++ b/openair1/PHY/CODING/nr_polar_init.c
@@ -32,6 +32,7 @@
#include "PHY/CODING/nrPolar_tools/nr_polar_defs.h"
#include "PHY/NR_TRANSPORT/nr_dci.h"
+#include "nrPolar_tools/nr_polar_psbch_defs.h"
#define PolarKey ((messageType<<24)|(messageLength<<8)|aggregation_level)
static t_nrPolar_params * PolarList=NULL;
@@ -190,7 +191,22 @@ t_nrPolar_params *nr_polar_params(int8_t messageType, uint16_t messageLength, ui
newPolarInitNode->payloadBits = messageLength;
newPolarInitNode->crcCorrectionBits = NR_POLAR_PUCCH_CRC_ERROR_CORRECTION_BITS;
//LOG_D(PHY,"New polar node, encoderLength %d, aggregation_level %d\n",newPolarInitNode->encoderLength,aggregation_level);
-
+ } else if (messageType == SL_NR_POLAR_PSBCH_MESSAGE_TYPE) { // PSBCH
+ newPolarInitNode->n_max = SL_NR_POLAR_PSBCH_N_MAX;
+ newPolarInitNode->i_il = SL_NR_POLAR_PSBCH_I_IL;
+ newPolarInitNode->i_seg = SL_NR_POLAR_PSBCH_I_SEG;
+ newPolarInitNode->n_pc = SL_NR_POLAR_PSBCH_N_PC;
+ newPolarInitNode->n_pc_wm = SL_NR_POLAR_PSBCH_N_PC_WM;
+ newPolarInitNode->i_bil = SL_NR_POLAR_PSBCH_I_BIL;
+ newPolarInitNode->crcParityBits = SL_NR_POLAR_PSBCH_CRC_PARITY_BITS;
+ newPolarInitNode->payloadBits = SL_NR_POLAR_PSBCH_PAYLOAD_BITS;
+ newPolarInitNode->encoderLength = SL_NR_POLAR_PSBCH_E_NORMAL_CP + 2;
+ newPolarInitNode->crcCorrectionBits = SL_NR_POLAR_PSBCH_CRC_ERROR_CORRECTION_BITS;
+ newPolarInitNode->crc_generator_matrix = crc24c_generator_matrix(newPolarInitNode->payloadBits); // G_P
+ LOG_D(PHY,
+ "SIDELINK: Initializing polar parameters for PSBCH (K %d, E %d)\n",
+ newPolarInitNode->payloadBits,
+ newPolarInitNode->encoderLength);
} else {
AssertFatal(1 == 0, "[nr_polar_init] Incorrect Message Type(%d)", messageType);
}
diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c
index 641c8a1f67..48152db15b 100644
--- a/openair1/PHY/INIT/nr_init_ue.c
+++ b/openair1/PHY/INIT/nr_init_ue.c
@@ -32,7 +32,7 @@
#include "PHY/NR_REFSIG/ul_ref_seq_nr.h"
#include "PHY/NR_REFSIG/refsig_defs_ue.h"
#include "PHY/NR_REFSIG/nr_refsig.h"
-#include "PHY/MODULATION/nr_modulation.h"
+#include "PHY/NR_REFSIG/nr_mod_table.h"
#include "openair2/COMMON/prs_nr_paramdef.h"
#include "SCHED_NR_UE/harq_nr.h"
@@ -384,6 +384,15 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB)
return 0;
}
+static void sl_ue_free(PHY_VARS_NR_UE *UE)
+{
+ if (UE->SL_UE_PHY_PARAMS.init_params.sl_pss_for_correlation) {
+ free_and_zero(UE->SL_UE_PHY_PARAMS.init_params.sl_pss_for_correlation[0]);
+ free_and_zero(UE->SL_UE_PHY_PARAMS.init_params.sl_pss_for_correlation[1]);
+ free_and_zero(UE->SL_UE_PHY_PARAMS.init_params.sl_pss_for_correlation);
+ }
+}
+
void term_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB)
{
const NR_DL_FRAME_PARMS* fp = &ue->frame_parms;
@@ -489,6 +498,8 @@ void term_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB)
free_and_zero(ue->prs_vars[idx]);
}
+
+ sl_ue_free(ue);
}
void free_nr_ue_dl_harq(NR_DL_UE_HARQ_t harq_list[2][NR_MAX_DLSCH_HARQ_PROCESSES], int number_of_processes, int num_rb) {
@@ -702,3 +713,70 @@ void phy_term_nr_top(void)
free_ul_reference_signal_sequences();
free_context_synchro_nr();
}
+
+static void sl_generate_psbch_dmrs_qpsk_sequences(PHY_VARS_NR_UE *UE, struct complex16 *modulated_dmrs_sym, uint16_t slss_id)
+{
+ uint8_t idx = 0;
+ uint32_t *sl_dmrs_sequence = UE->SL_UE_PHY_PARAMS.init_params.psbch_dmrs_gold_sequences[slss_id];
+ c16_t *mod_table = (c16_t *)nr_qpsk_mod_table;
+
+#ifdef SL_DEBUG_INIT
+ printf("SIDELINK INIT: PSBCH DMRS Generation with slss_id:%d\n", slss_id);
+#endif
+
+ /// QPSK modulation
+ for (int m = 0; m < SL_NR_NUM_PSBCH_DMRS_RE; m++) {
+ idx = (((sl_dmrs_sequence[(m << 1) >> 5]) >> ((m << 1) & 0x1f)) & 3);
+ modulated_dmrs_sym[m].r = mod_table[idx].r;
+ modulated_dmrs_sym[m].i = mod_table[idx].i;
+
+#ifdef SL_DEBUG_INIT_DATA
+ printf("m:%d gold seq: %d b0-b1: %d-%d DMRS Symbols: %d %d\n",
+ m,
+ sl_dmrs_sequence[(m << 1) >> 5],
+ (((sl_dmrs_sequence[(m << 1) >> 5]) >> ((m << 1) & 0x1f)) & 1),
+ (((sl_dmrs_sequence[((m << 1) + 1) >> 5]) >> (((m << 1) + 1) & 0x1f)) & 1),
+ modulated_dmrs_sym[m].r,
+ modulated_dmrs_sym[m].i);
+ printf("idx:%d, qpsk_table.r:%d, qpsk_table.i:%d\n", idx, mod_table[idx].r, mod_table[idx].i);
+#endif
+ }
+
+#ifdef SL_DUMP_INIT_SAMPLES
+ char filename[40], varname[25];
+ sprintf(filename, "sl_psbch_dmrs_slssid_%d.m", slss_id);
+ sprintf(varname, "sl_dmrs_id_%d.m", slss_id);
+ LOG_M(filename, varname, (void *)modulated_dmrs_sym, SL_NR_NUM_PSBCH_DMRS_RE, 1, 1);
+#endif
+}
+
+void sl_ue_phy_init(PHY_VARS_NR_UE *UE)
+{
+ uint16_t scaling_value = ONE_OVER_SQRT2_Q15;
+
+ NR_DL_FRAME_PARMS *sl_fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params;
+
+ if (!UE->SL_UE_PHY_PARAMS.init_params.sl_pss_for_correlation) {
+ UE->SL_UE_PHY_PARAMS.init_params.sl_pss_for_correlation = malloc16_clear(SL_NR_NUM_IDs_IN_PSS * sizeof(int32_t *));
+ UE->SL_UE_PHY_PARAMS.init_params.sl_pss_for_correlation[0] = malloc16_clear(sizeof(int32_t) * sl_fp->ofdm_symbol_size);
+ UE->SL_UE_PHY_PARAMS.init_params.sl_pss_for_correlation[1] = malloc16_clear(sizeof(int32_t) * sl_fp->ofdm_symbol_size);
+ }
+ LOG_I(PHY, "SIDELINK INIT: GENERATE PSS, SSS, GOLD SEQUENCES AND PSBCH DMRS SEQUENCES FOR ALL possible SLSS IDs 0- 671\n");
+
+ // Generate PSS sequences for IDs 0,1 used in PSS
+ sl_generate_pss(&UE->SL_UE_PHY_PARAMS.init_params, 0, scaling_value);
+ sl_generate_pss(&UE->SL_UE_PHY_PARAMS.init_params, 1, scaling_value);
+
+ // Generate psbch dmrs Gold Sequences and modulated dmrs symbols
+ sl_init_psbch_dmrs_gold_sequences(UE);
+ for (int slss_id = 0; slss_id < SL_NR_NUM_SLSS_IDs; slss_id++) {
+ sl_generate_psbch_dmrs_qpsk_sequences(UE, UE->SL_UE_PHY_PARAMS.init_params.psbch_dmrs_modsym[slss_id], slss_id);
+ sl_generate_sss(&UE->SL_UE_PHY_PARAMS.init_params, slss_id, scaling_value);
+ }
+
+ // Generate PSS time domain samples used for correlation during SLSS reception.
+ sl_generate_pss_ifft_samples(&UE->SL_UE_PHY_PARAMS, &UE->SL_UE_PHY_PARAMS.init_params);
+
+ init_symbol_rotation(sl_fp);
+ init_timeshift_rotation(sl_fp);
+}
diff --git a/openair1/PHY/INIT/nr_parms.c b/openair1/PHY/INIT/nr_parms.c
index 5b45bbc8f9..dc516d7f24 100644
--- a/openair1/PHY/INIT/nr_parms.c
+++ b/openair1/PHY/INIT/nr_parms.c
@@ -425,7 +425,7 @@ void nr_dump_frame_parms(NR_DL_FRAME_PARMS *fp)
LOG_I(PHY,"fp->samples_per_frame=%d\n",fp->samples_per_frame);
LOG_I(PHY,"fp->dl_CarrierFreq=%lu\n",fp->dl_CarrierFreq);
LOG_I(PHY,"fp->ul_CarrierFreq=%lu\n",fp->ul_CarrierFreq);
+ LOG_I(PHY, "fp->Nid_cell=%d\n", fp->Nid_cell);
+ LOG_I(PHY, "fp->first_carrier_offset=%d\n", fp->first_carrier_offset);
+ LOG_I(PHY, "fp->ssb_start_subcarrier=%d\n", fp->ssb_start_subcarrier);
}
-
-
-
diff --git a/openair1/PHY/INIT/nr_phy_init.h b/openair1/PHY/INIT/nr_phy_init.h
index 5e36558e2b..a39f931de1 100644
--- a/openair1/PHY/INIT/nr_phy_init.h
+++ b/openair1/PHY/INIT/nr_phy_init.h
@@ -61,4 +61,5 @@ void init_delay_table(uint16_t ofdm_symbol_size,
int max_ofdm_symbol_size,
c16_t delay_table[][max_ofdm_symbol_size]);
+void sl_ue_phy_init(PHY_VARS_NR_UE *UE);
#endif
diff --git a/openair1/PHY/MODULATION/modulation_UE.h b/openair1/PHY/MODULATION/modulation_UE.h
index 07d7625770..03c13db8ae 100644
--- a/openair1/PHY/MODULATION/modulation_UE.h
+++ b/openair1/PHY/MODULATION/modulation_UE.h
@@ -49,9 +49,17 @@ int slot_fep(PHY_VARS_UE *phy_vars_ue,
int reset_freq_est);
int nr_slot_fep(PHY_VARS_NR_UE *ue,
+ NR_DL_FRAME_PARMS *frame_parms,
const UE_nr_rxtx_proc_t *proc,
unsigned char symbol,
- c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]);
+ c16_t rxdataF[][frame_parms->samples_per_slot_wCP],
+ uint32_t linktype);
+int sl_nr_slot_fep(PHY_VARS_NR_UE *ue,
+ UE_nr_rxtx_proc_t *proc,
+ unsigned char symbol,
+ unsigned char Ns,
+ uint32_t sample_offset,
+ c16_t rxdataF[][ue->SL_UE_PHY_PARAMS.sl_frame_params.samples_per_slot_wCP]);
int nr_slot_fep_init_sync(PHY_VARS_NR_UE *ue,
const UE_nr_rxtx_proc_t *proc,
diff --git a/openair1/PHY/MODULATION/slot_fep_nr.c b/openair1/PHY/MODULATION/slot_fep_nr.c
index a645d9ded1..4024796bb0 100644
--- a/openair1/PHY/MODULATION/slot_fep_nr.c
+++ b/openair1/PHY/MODULATION/slot_fep_nr.c
@@ -34,12 +34,109 @@
#define LOG_I(A,B...) printf(A)
#endif*/
+int sl_nr_slot_fep(PHY_VARS_NR_UE *ue,
+ UE_nr_rxtx_proc_t *proc,
+ unsigned char symbol,
+ unsigned char Ns,
+ uint32_t sample_offset,
+ c16_t rxdataF[][ue->SL_UE_PHY_PARAMS.sl_frame_params.samples_per_slot_wCP])
+{
+ NR_DL_FRAME_PARMS *frame_params = &ue->SL_UE_PHY_PARAMS.sl_frame_params;
+ NR_UE_COMMON *common_vars = &ue->common_vars;
+
+ AssertFatal(symbol < frame_params->symbols_per_slot,
+ "slot_fep: symbol must be between 0 and %d\n",
+ frame_params->symbols_per_slot - 1);
+ AssertFatal(Ns < frame_params->slots_per_frame, "slot_fep: Ns must be between 0 and %d\n", frame_params->slots_per_frame - 1);
+
+ unsigned int nb_prefix_samples = frame_params->nb_prefix_samples;
+ unsigned int nb_prefix_samples0 = frame_params->nb_prefix_samples0;
+
+ dft_size_idx_t dftsize = get_dft(frame_params->ofdm_symbol_size);
+ // This is for misalignment issues
+ int32_t tmp_dft_in[8192] __attribute__((aligned(32)));
+
+ unsigned int rx_offset = frame_params->get_samples_slot_timestamp(Ns, frame_params, 0);
+ unsigned int abs_symbol = Ns * frame_params->symbols_per_slot + symbol;
+
+ rx_offset += sample_offset;
+
+ for (int idx_symb = Ns * frame_params->symbols_per_slot; idx_symb <= abs_symbol; idx_symb++)
+ rx_offset += (idx_symb % (0x7 << frame_params->numerology_index)) ? nb_prefix_samples : nb_prefix_samples0;
+ rx_offset += frame_params->ofdm_symbol_size * symbol;
+
+ // use OFDM symbol from within 1/8th of the CP to avoid ISI
+ rx_offset -= (nb_prefix_samples / frame_params->ofdm_offset_divisor);
+
+#ifdef SL_DEBUG_SLOT_FEP
+ // if (ue->frame <100)
+ LOG_I(PHY,
+ "slot_fep: slot %d, symbol %d, nb_prefix_samples %u, nb_prefix_samples0 %u, rx_offset %u\n",
+ Ns,
+ symbol,
+ nb_prefix_samples,
+ nb_prefix_samples0,
+ rx_offset);
+#endif
+
+ for (unsigned char aa = 0; aa < frame_params->nb_antennas_rx; aa++) {
+ memset(&rxdataF[aa][frame_params->ofdm_symbol_size * symbol], 0, frame_params->ofdm_symbol_size * sizeof(int32_t));
+
+ int16_t *rxdata_ptr = (int16_t *)&common_vars->rxdata[aa][rx_offset];
+
+ // if input to dft is not 256-bit aligned
+ if ((rx_offset & 7) != 0) {
+ memcpy((void *)&tmp_dft_in[0], (void *)&common_vars->rxdata[aa][rx_offset], frame_params->ofdm_symbol_size * sizeof(int32_t));
+
+ rxdata_ptr = (int16_t *)tmp_dft_in;
+ }
+
+ dft(dftsize, rxdata_ptr, (int16_t *)&rxdataF[aa][frame_params->ofdm_symbol_size * symbol], 1);
+
+ int symb_offset = (Ns % frame_params->slots_per_subframe) * frame_params->symbols_per_slot;
+ int32_t rot2 = ((uint32_t *)frame_params->symbol_rotation[1])[symbol + symb_offset];
+ ((int16_t *)&rot2)[1] = -((int16_t *)&rot2)[1];
+
+#ifdef SL_DEBUG_SLOT_FEP
+ // if (ue->frame <100)
+ LOG_I(PHY,
+ "slot_fep: slot %d, symbol %d rx_offset %u, rotation symbol %d %d.%d\n",
+ Ns,
+ symbol,
+ rx_offset,
+ symbol + symb_offset,
+ ((int16_t *)&rot2)[0],
+ ((int16_t *)&rot2)[1]);
+#endif
+
+ rotate_cpx_vector((c16_t *)&rxdataF[aa][frame_params->ofdm_symbol_size * symbol],
+ (c16_t *)&rot2,
+ (c16_t *)&rxdataF[aa][frame_params->ofdm_symbol_size * symbol],
+ frame_params->ofdm_symbol_size,
+ 15);
+
+ int16_t *shift_rot = (int16_t *)frame_params->timeshift_symbol_rotation;
+
+ multadd_cpx_vector((int16_t *)&rxdataF[aa][frame_params->ofdm_symbol_size * symbol],
+ shift_rot,
+ (int16_t *)&rxdataF[aa][frame_params->ofdm_symbol_size * symbol],
+ 1,
+ frame_params->ofdm_symbol_size,
+ 15);
+ }
+
+ LOG_D(PHY, "SIDELINK RX: Slot FEP: done for symbol:%d\n", symbol);
+
+ return 0;
+}
+
int nr_slot_fep(PHY_VARS_NR_UE *ue,
+ NR_DL_FRAME_PARMS *frame_parms,
const UE_nr_rxtx_proc_t *proc,
unsigned char symbol,
- c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP])
+ c16_t rxdataF[][frame_parms->samples_per_slot_wCP],
+ uint32_t linktype)
{
- NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
NR_UE_COMMON *common_vars = &ue->common_vars;
int Ns = proc->nr_slot_rx;
@@ -96,14 +193,7 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue,
stop_meas(&ue->rx_dft_stats);
- apply_nr_rotation_RX(frame_parms,
- rxdataF[aa],
- frame_parms->symbol_rotation[0],
- Ns,
- frame_parms->N_RB_DL,
- 0,
- symbol,
- 1);
+ apply_nr_rotation_RX(frame_parms, rxdataF[aa], frame_parms->symbol_rotation[linktype], Ns, frame_parms->N_RB_DL, 0, symbol, 1);
}
#ifdef DEBUG_FEP
diff --git a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c
index e9a90fc011..70381d0bd8 100644
--- a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c
+++ b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c
@@ -202,22 +202,27 @@ int nr_pdcch_dmrs_rx(PHY_VARS_NR_UE *ue,
return(0);
}
-void nr_pbch_dmrs_rx(const int symbol, const unsigned int *nr_gold_pbch, c16_t *output)
+void nr_pbch_dmrs_rx(int symbol, unsigned int *nr_gold_pbch, c16_t *output, bool sidelink)
{
int m,m0,m1;
uint8_t idx=0;
- AssertFatal(symbol>=0 && symbol <3,"illegal symbol %d\n",symbol);
- if (symbol == 0) {
- m0=0;
- m1=60;
- }
- else if (symbol == 1) {
- m0=60;
- m1=84;
- }
- else {
- m0=84;
- m1=144;
+ if (sidelink) {
+ AssertFatal(symbol == 0 || (symbol >= 5 && symbol <= 12), "illegal symbol %d\n", symbol);
+ m0 = (symbol) ? (symbol - 4) * 33 : 0;
+ m1 = (symbol) ? (symbol - 3) * 33 : 33;
+
+ } else {
+ AssertFatal(symbol >= 0 && symbol < 3, "illegal symbol %d\n", symbol);
+ if (symbol == 0) {
+ m0 = 0;
+ m1 = 60;
+ } else if (symbol == 1) {
+ m0 = 60;
+ m1 = 84;
+ } else {
+ m0 = 84;
+ m1 = 144;
+ }
}
// printf("Generating pilots symbol %d, m0 %d, m1 %d\n",symbol,m0,m1);
/// QPSK modulation
diff --git a/openair1/PHY/NR_REFSIG/nr_gold_ue.c b/openair1/PHY/NR_REFSIG/nr_gold_ue.c
index 122cc11dd3..56dd09c0f0 100644
--- a/openair1/PHY/NR_REFSIG/nr_gold_ue.c
+++ b/openair1/PHY/NR_REFSIG/nr_gold_ue.c
@@ -148,3 +148,28 @@ void init_nr_gold_prs(PHY_VARS_NR_UE* ue)
} // for rsc
} // for gnb
}
+
+void sl_init_psbch_dmrs_gold_sequences(PHY_VARS_NR_UE *UE)
+{
+ unsigned int x1, x2;
+ uint16_t slss_id;
+ uint8_t reset;
+
+ for (slss_id = 0; slss_id < SL_NR_NUM_SLSS_IDs; slss_id++) {
+ reset = 1;
+ x2 = slss_id;
+
+#ifdef SL_DEBUG_INIT
+ printf("\nPSBCH DMRS GOLD SEQ for SLSSID :%d :\n", slss_id);
+#endif
+
+ for (uint8_t n = 0; n < SL_NR_NUM_PSBCH_DMRS_RE_DWORD; n++) {
+ UE->SL_UE_PHY_PARAMS.init_params.psbch_dmrs_gold_sequences[slss_id][n] = lte_gold_generic(&x1, &x2, reset);
+ reset = 0;
+
+#ifdef SL_DEBUG_INIT_DATA
+ printf("%x\n", SL_UE_INIT_PARAMS.sl_psbch_dmrs_gold_sequences[slss_id][n]);
+#endif
+ }
+ }
+}
diff --git a/openair1/PHY/NR_REFSIG/refsig_defs_ue.h b/openair1/PHY/NR_REFSIG/refsig_defs_ue.h
index 81c72f59ef..c7919130b5 100644
--- a/openair1/PHY/NR_REFSIG/refsig_defs_ue.h
+++ b/openair1/PHY/NR_REFSIG/refsig_defs_ue.h
@@ -30,7 +30,7 @@
/*!\brief This function generates the NR Gold sequence (38-211, Sec 5.2.1) for the PBCH DMRS.
@param PHY_VARS_NR_UE* ue structure provides configuration, frame parameters and the pointers to the 32 bits sequence storage tables
*/
-void nr_pbch_dmrs_rx(const int dmrss, const unsigned int *nr_gold_pbch, c16_t *output);
+void nr_pbch_dmrs_rx(int dmrss, unsigned int *nr_gold_pbch, c16_t *output, bool sidelink);
/*!\brief This function generates the NR Gold sequence (38-211, Sec 5.2.1) for the PDCCH DMRS.
@param PHY_VARS_NR_UE* ue structure provides configuration, frame parameters and the pointers to the 32 bits sequence storage tables
@@ -66,5 +66,8 @@ void nr_init_pusch_dmrs(PHY_VARS_NR_UE* ue,
void nr_init_csi_rs(const NR_DL_FRAME_PARMS *fp, uint32_t ***csi_rs, uint32_t Nid);
void init_nr_gold_prs(PHY_VARS_NR_UE* ue);
-
+void sl_generate_pss(SL_NR_UE_INIT_PARAMS_t *sl_init_params, uint8_t n_sl_id2, uint16_t scaling);
+void sl_generate_pss_ifft_samples(sl_nr_ue_phy_params_t *sl_ue_params, SL_NR_UE_INIT_PARAMS_t *sl_init_params);
+void sl_generate_sss(SL_NR_UE_INIT_PARAMS_t *sl_init_params, uint16_t slss_id, uint16_t scaling);
+void sl_init_psbch_dmrs_gold_sequences(PHY_VARS_NR_UE *UE);
#endif
diff --git a/openair1/PHY/NR_REFSIG/sss_nr.h b/openair1/PHY/NR_REFSIG/sss_nr.h
index 129c5a5917..1dff912889 100644
--- a/openair1/PHY/NR_REFSIG/sss_nr.h
+++ b/openair1/PHY/NR_REFSIG/sss_nr.h
@@ -67,8 +67,28 @@
#define PHASE_HYPOTHESIS_NUMBER (16)
#define INDEX_NO_PHASE_DIFFERENCE (3) /* this is for no phase shift case */
+
/************** FUNCTION ******************************************/
+static const c16_t phase_nr[PHASE_HYPOTHESIS_NUMBER] = {
+ // {pi/3 ---- pi/3, -pi/3 ---- pi/3}
+ {16384, -28377},
+ {20173, -25821},
+ {23571, -22762},
+ {26509, -19260},
+ {28932, -15383},
+ {30791, -11207},
+ {32051, -6813},
+ {32687, -2286},
+ {32687, 2286},
+ {32051, 6813},
+ {30791, 11207},
+ {28932, 15383},
+ {26509, 19260},
+ {23571, 22762},
+ {20173, 25821},
+ {16384, 28377}};
+
void init_context_sss_nr(int amp);
void free_context_sss_nr(void);
diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c
index fc89fdafd9..7e44335225 100644
--- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c
+++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c
@@ -581,7 +581,7 @@ c32_t nr_pbch_dmrs_correlation(const PHY_VARS_NR_UE *ue,
// generate pilot
// Note: pilot returned by the following function is already the complex conjugate of the transmitted DMRS
c16_t pilot[200] __attribute__((aligned(16)));
- nr_pbch_dmrs_rx(dmrss, nr_gold_pbch, pilot);
+ nr_pbch_dmrs_rx(dmrss, (uint32_t *)nr_gold_pbch, pilot, false);
c32_t computed_val = {0};
for (int aarx=0; aarxframe_parms.nb_antennas_rx; aarx++) {
@@ -641,43 +641,66 @@ c32_t nr_pbch_dmrs_correlation(const PHY_VARS_NR_UE *ue,
}
int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue,
+ NR_DL_FRAME_PARMS *fp,
int estimateSz,
struct complex16 dl_ch_estimates[][estimateSz],
- struct complex16 dl_ch_estimates_time[][ue->frame_parms.ofdm_symbol_size],
+ struct complex16 dl_ch_estimates_time[][fp->ofdm_symbol_size],
const UE_nr_rxtx_proc_t *proc,
unsigned char symbol,
int dmrss,
uint8_t ssb_index,
uint8_t n_hf,
- c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP])
+ c16_t rxdataF[][fp->samples_per_slot_wCP],
+ bool sidelink,
+ uint16_t Nid)
{
int Ns = proc->nr_slot_rx;
c16_t pilot[200] __attribute__((aligned(16)));
//int slot_pbch;
- const int nushift = ue->frame_parms.Nid_cell % 4;
- unsigned int ssb_offset = ue->frame_parms.first_carrier_offset + ue->frame_parms.ssb_start_subcarrier;
- if (ssb_offset>= ue->frame_parms.ofdm_symbol_size) ssb_offset-=ue->frame_parms.ofdm_symbol_size;
+ uint8_t nushift = 0, lastsymbol = 0, num_rbs = 0;
+ uint32_t *gold_seq = NULL;
- const int ch_offset = ue->frame_parms.ofdm_symbol_size * symbol;
+ if (sidelink) {
+ AssertFatal(dmrss == 0 || (dmrss >= 5 && dmrss <= 12), "symbol %d is illegal for PSBCH DM-RS \n", dmrss);
- AssertFatal(dmrss >= 0 && dmrss < 3,
- "symbol %d is illegal for PBCH DM-RS \n",
- dmrss);
+ sl_nr_ue_phy_params_t *sl_phy_params = &ue->SL_UE_PHY_PARAMS;
- const int symbol_offset = ue->frame_parms.ofdm_symbol_size * symbol;
+ LOG_D(PHY, "PSBCH Channel Estimation SLSSID:%d\n", Nid);
+ gold_seq = sl_phy_params->init_params.psbch_dmrs_gold_sequences[Nid];
+ lastsymbol = 12;
+ num_rbs = SL_NR_NUM_PSBCH_RBS_IN_ONE_SYMBOL;
+
+ } else {
+ nushift = fp->Nid_cell % 4;
+
+ AssertFatal(dmrss >= 0 && dmrss < 3, "symbol %d is illegal for PBCH DM-RS \n", dmrss);
+
+ gold_seq = ue->nr_gold_pbch[n_hf][ssb_index];
+ lastsymbol = 2;
+ num_rbs = 20;
+ }
+
+ unsigned int ssb_offset = fp->first_carrier_offset + fp->ssb_start_subcarrier;
+ if (ssb_offset >= fp->ofdm_symbol_size)
+ ssb_offset -= fp->ofdm_symbol_size;
+
+ const int ch_offset = fp->ofdm_symbol_size * symbol;
+ const int symbol_offset = fp->ofdm_symbol_size * symbol;
const int k = nushift;
- const c16_t *fl, *fm, *fr;
+
DEBUG_PBCH("PBCH Channel Estimation : gNB_id %d ch_offset %d, OFDM size %d, Ncp=%d, Ns=%d, k=%d symbol %d\n",
proc->gNB_id,
ch_offset,
- ue->frame_parms.ofdm_symbol_size,
- ue->frame_parms.Ncp,
+ fp->ofdm_symbol_size,
+ fp->Ncp,
Ns,
k,
symbol);
+ const c16_t *fl, *fm, *fr;
+
switch (k) {
case 0:
fl = filt16a_l0;
@@ -709,22 +732,20 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue,
break;
}
-
// generate pilot
// Note: pilot returned by the following function is already the complex conjugate of the transmitted DMRS
- nr_pbch_dmrs_rx(dmrss, ue->nr_gold_pbch[n_hf][ssb_index], pilot);
-
- for (int aarx=0; aarxframe_parms.nb_antennas_rx; aarx++) {
+ nr_pbch_dmrs_rx(dmrss, gold_seq, &pilot[0], sidelink);
+ for (int aarx = 0; aarx < fp->nb_antennas_rx; aarx++) {
int re_offset = ssb_offset;
c16_t *pil = pilot;
c16_t *rxF = &rxdataF[aarx][symbol_offset + k];
c16_t *dl_ch = &dl_ch_estimates[aarx][ch_offset];
- memset(dl_ch, 0, sizeof(c16_t) * ue->frame_parms.ofdm_symbol_size);
+ memset(dl_ch, 0, sizeof(c16_t) * fp->ofdm_symbol_size);
- DEBUG_PBCH("pbch ch est pilot RB_DL %d\n", ue->frame_parms.N_RB_DL);
- DEBUG_PBCH("k %d, first_carrier %d\n", k, ue->frame_parms.first_carrier_offset);
+ DEBUG_PBCH("pbch ch est pilot RB_DL %d\n", fp->N_RB_DL);
+ DEBUG_PBCH("k %d, first_carrier %d\n", k, fp->first_carrier_offset);
// Treat first 2 pilots specially (left edge)
c16_t ch;
@@ -732,31 +753,31 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue,
DEBUG_PBCH("pilot 0: rxF= (%d,%d), ch= (%d,%d), pil=(%d,%d)\n", rxF[re_offset].r, rxF[re_offset].i, ch.r, ch.i, pil->r, pil->i);
multaddRealVectorComplexScalar(fl, ch, dl_ch, 16);
pil++;
- re_offset = (re_offset + 4) % ue->frame_parms.ofdm_symbol_size;
+ re_offset = (re_offset + 4) % fp->ofdm_symbol_size;
ch = c16mulShift(*pil, rxF[re_offset], 15);
DEBUG_PBCH("pilot 1: rxF= (%d,%d), ch= (%d,%d), pil=(%d,%d)\n", rxF[re_offset].r, rxF[re_offset].i, ch.r, ch.i, pil->r, pil->i);
multaddRealVectorComplexScalar(fm, ch, dl_ch, 16);
pil++;
- re_offset = (re_offset + 4) % ue->frame_parms.ofdm_symbol_size;
+ re_offset = (re_offset + 4) % fp->ofdm_symbol_size;
ch = c16mulShift(*pil, rxF[re_offset], 15);
DEBUG_PBCH("pilot 2: rxF= (%d,%d), ch= (%d,%d), pil=(%d,%d)\n", rxF[re_offset].r, rxF[re_offset].i, ch.r, ch.i, pil->r, pil->i);
multaddRealVectorComplexScalar(fr, ch, dl_ch, 16);
pil++;
- re_offset = (re_offset + 4) % ue->frame_parms.ofdm_symbol_size;
+ re_offset = (re_offset + 4) % fp->ofdm_symbol_size;
dl_ch += 24;
- for (int pilot_cnt = 3; pilot_cnt < (3 * 20); pilot_cnt += 3) {
+ for (int pilot_cnt = 3; pilot_cnt < (3 * num_rbs); pilot_cnt += 3) {
// if (pilot_cnt == 30)
// rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k)];
// in 2nd symbol, skip middle REs (48 with DMRS, 144 for SSS, and another 48 with DMRS)
if (dmrss == 1 && pilot_cnt == 12) {
pilot_cnt=48;
- re_offset = (re_offset + 144) % ue->frame_parms.ofdm_symbol_size;
+ re_offset = (re_offset + 144) % fp->ofdm_symbol_size;
dl_ch += 288;
}
ch = c16mulShift(*pil, rxF[re_offset], 15);
@@ -771,7 +792,7 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue,
multaddRealVectorComplexScalar(fl, ch, dl_ch, 16);
pil++;
- re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size;
+ re_offset = (re_offset + 4) % fp->ofdm_symbol_size;
ch = c16mulShift(*pil, rxF[re_offset], 15);
DEBUG_PBCH("pilot %u: rxF= (%d,%d), ch= (%d,%d), pil=(%d,%d)\n",
pilot_cnt + 1,
@@ -783,7 +804,7 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue,
pil->i);
multaddRealVectorComplexScalar(fm, ch, dl_ch, 16);
pil++;
- re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size;
+ re_offset = (re_offset + 4) % fp->ofdm_symbol_size;
ch = c16mulShift(*pil, rxF[re_offset], 15);
DEBUG_PBCH("pilot %u: rxF= (%d,%d), ch= (%d,%d), pil=(%d,%d)\n",
pilot_cnt + 2,
@@ -795,11 +816,11 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue,
pil->i);
multaddRealVectorComplexScalar(fr, ch, dl_ch, 16);
pil++;
- re_offset = (re_offset + 4) % ue->frame_parms.ofdm_symbol_size;
+ re_offset = (re_offset + 4) % fp->ofdm_symbol_size;
dl_ch += 12;
}
- if( dmrss == 2) // update time statistics for last PBCH symbol
+ if (dmrss == lastsymbol) // update time statistics for last PBCH symbol
{
// do ifft of channel estimate
LOG_D(PHY,"Channel Impulse Computation Slot %d Symbol %d ch_offset %d\n", Ns, symbol, ch_offset);
@@ -809,14 +830,8 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue,
}
}
- if (dmrss == 2)
- UEscopeCopy(ue,
- pbchDlChEstimateTime,
- (void *)dl_ch_estimates_time,
- sizeof(c16_t),
- ue->frame_parms.nb_antennas_rx,
- ue->frame_parms.ofdm_symbol_size,
- 0);
+ if (!sidelink && dmrss == lastsymbol)
+ UEscopeCopy(ue, pbchDlChEstimateTime, (void *)dl_ch_estimates_time, sizeof(c16_t), fp->nb_antennas_rx, fp->ofdm_symbol_size, 0);
return(0);
}
diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h
index 5b1e43f953..157250a413 100644
--- a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h
+++ b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h
@@ -65,15 +65,18 @@ c32_t nr_pbch_dmrs_correlation(const PHY_VARS_NR_UE *ue,
const c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]);
int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue,
+ NR_DL_FRAME_PARMS *fp,
int estimateSz,
struct complex16 dl_ch_estimates[][estimateSz],
- struct complex16 dl_ch_estimates_time[][ue->frame_parms.ofdm_symbol_size],
+ struct complex16 dl_ch_estimates_time[][fp->ofdm_symbol_size],
const UE_nr_rxtx_proc_t *proc,
unsigned char symbol,
int dmrss,
uint8_t ssb_index,
uint8_t n_hf,
- c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]);
+ c16_t rxdataF[][fp->samples_per_slot_wCP],
+ bool sidelink,
+ uint16_t Nid);
int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue,
const UE_nr_rxtx_proc_t *proc,
@@ -137,5 +140,10 @@ void nr_pdsch_ptrs_processing(PHY_VARS_NR_UE *ue,
NR_UE_DLSCH_t dlsch[2]);
float_t get_nr_RSRP(module_id_t Mod_id,uint8_t CC_id,uint8_t gNB_index);
+
+void nr_sl_psbch_rsrp_measurements(sl_nr_ue_phy_params_t *sl_phy_params,
+ NR_DL_FRAME_PARMS *fp,
+ c16_t rxdataF[][fp->samples_per_slot_wCP],
+ bool use_SSS);
/** @}*/
#endif
diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c b/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c
index c21a6a709f..8cd33743dd 100644
--- a/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c
+++ b/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c
@@ -309,3 +309,51 @@ void nr_ue_rrc_measurements(PHY_VARS_NR_UE *ue,
ue->measurements.n0_power_tot_dB + 30 - 10 * log10(pow(2, 30)) - dB_fixed(ue->frame_parms.ofdm_symbol_size)
- ((int)rx_gain - (int)rx_gain_offset));
}
+
+// PSBCH RSRP calculations according to 38.215 section 5.1.22
+void nr_sl_psbch_rsrp_measurements(sl_nr_ue_phy_params_t *sl_phy_params,
+ NR_DL_FRAME_PARMS *fp,
+ c16_t rxdataF[][fp->samples_per_slot_wCP],
+ bool use_SSS)
+{
+ SL_NR_UE_PSBCH_t *psbch_rx = &sl_phy_params->psbch;
+ uint8_t numsym = (fp->Ncp) ? SL_NR_NUM_SYMBOLS_SSB_EXT_CP : SL_NR_NUM_SYMBOLS_SSB_NORMAL_CP;
+ uint32_t re_offset = fp->first_carrier_offset + fp->ssb_start_subcarrier;
+ uint32_t rsrp = 0, num_re = 0;
+
+ LOG_D(PHY, "PSBCH RSRP MEAS: numsym:%d, re_offset:%d\n", numsym, re_offset);
+
+ for (int aarx = 0; aarx < fp->nb_antennas_rx; aarx++) {
+ // Calculate PSBCH RSRP based from DMRS REs
+ for (uint8_t symbol = 0; symbol < numsym;) {
+ struct complex16 *rxF = &rxdataF[aarx][symbol * fp->ofdm_symbol_size];
+
+ for (int re = 0; re < SL_NR_NUM_PSBCH_RE_IN_ONE_SYMBOL; re++) {
+ if (re % 4 == 0) { // DMRS RE
+ uint16_t offset = (re_offset + re) % fp->ofdm_symbol_size;
+
+ rsrp += c16amp2(rxF[offset]);
+ num_re++;
+ }
+ }
+ symbol = (symbol == 0) ? 5 : symbol + 1;
+ }
+ }
+
+ if (use_SSS) {
+ // TBD...
+ // UE can decide between using only PSBCH DMRS or PSBCH DMRS and SSS for PSBCH RSRP computation.
+ // If needed this can be implemented. Reference Spec 38.215
+ }
+
+ psbch_rx->rsrp_dB_per_RE = 10 * log10(rsrp / num_re);
+ psbch_rx->rsrp_dBm_per_RE = psbch_rx->rsrp_dB_per_RE + 30 - 10 * log10(pow(2, 30))
+ - ((int)openair0_cfg[0].rx_gain[0] - (int)openair0_cfg[0].rx_gain_offset[0])
+ - dB_fixed(fp->ofdm_symbol_size);
+
+ LOG_I(PHY,
+ "PSBCH RSRP (DMRS REs): numREs:%d RSRP :%d dB/RE ,RSRP:%d dBm/RE\n",
+ num_re,
+ psbch_rx->rsrp_dB_per_RE,
+ psbch_rx->rsrp_dBm_per_RE);
+}
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c b/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c
index 97c3af3b1e..c8c7f6e73d 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c
@@ -101,6 +101,7 @@ static bool nr_pbch_detection(const UE_nr_rxtx_proc_t *proc,
for(int i=pbch_initial_symbol; iframe_parms,
estimateSz,
dl_ch_estimates,
dl_ch_estimates_time,
@@ -109,7 +110,9 @@ static bool nr_pbch_detection(const UE_nr_rxtx_proc_t *proc,
i - pbch_initial_symbol,
ssb->i_ssb,
ssb->n_hf,
- rxdataF);
+ rxdataF,
+ false,
+ frame_parms->Nid_cell);
stop_meas(&ue->dlsch_channel_estimation_stats);
fapiPbch_t result = {0};
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync_sl.c b/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync_sl.c
new file mode 100644
index 0000000000..e18a6a1633
--- /dev/null
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync_sl.c
@@ -0,0 +1,552 @@
+#include "PHY/defs_nr_UE.h"
+#include "PHY/TOOLS/tools_defs.h"
+#include "PHY/NR_REFSIG/sss_nr.h"
+#include "PHY/NR_UE_ESTIMATION/nr_estimation.h"
+#include "PHY/MODULATION/modulation_UE.h"
+#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
+#include "SCHED_NR_UE/defs.h"
+
+// Number of symbols carrying SLSS signal - PSS+SSS+PSBCH
+#define SL_NR_NUMSYM_SLSS_NORMAL_CP 14
+#define SL_NR_MAX_RX_ANTENNA 1
+#define SL_NR_FIRST_PSS_SYMBOL 1
+#define SL_NR_FIRST_SSS_SYMBOL 3
+#define SL_NR_NUM_PSS_SSS_SYMBOLS 4
+
+// #define SL_DEBUG
+
+static int sl_nr_pss_correlation(PHY_VARS_NR_UE *UE, int frame_index)
+{
+ sl_nr_ue_phy_params_t *sl_ue = &UE->SL_UE_PHY_PARAMS;
+ SL_NR_SYNC_PARAMS_t *sync_params = &sl_ue->sync_params;
+ NR_DL_FRAME_PARMS *sl_fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params;
+ int16_t **pss_for_correlation = (int16_t **)sl_ue->init_params.sl_pss_for_correlation;
+
+ uint32_t length = (frame_index == 0) ? sl_fp->samples_per_frame + (2 * sl_fp->ofdm_symbol_size) : sl_fp->samples_per_frame;
+ int32_t **rxdata = (int32_t **)UE->common_vars.rxdata;
+
+#ifdef SL_DEBUG
+ char fname[50], sname[25];
+ sprintf(fname, "rxdata_frame_%d.m", frame_index);
+ sprintf(sname, "rxd_frame%d", frame_index);
+ LOG_M(fname, sname, &rxdata[0][frame_index * sl_fp->samples_per_frame], sl_fp->samples_per_frame, 1, 1);
+ LOG_M("pss_for_correlation0.m", "pss_id0", pss_for_correlation[0], 2048, 1, 1);
+ LOG_M("pss_for_correlation1.m", "pss_id1", pss_for_correlation[1], 2048, 1, 1);
+
+ int64_t *pss_corr_debug_values[SL_NR_NUM_IDs_IN_PSS];
+#endif
+
+ int maxval = 0;
+ for (int i = 0; i < 2 * (sl_fp->ofdm_symbol_size); i++) {
+ maxval = max(maxval, pss_for_correlation[0][i]);
+ maxval = max(maxval, -pss_for_correlation[0][i]);
+ maxval = max(maxval, pss_for_correlation[1][i]);
+ maxval = max(maxval, -pss_for_correlation[1][i]);
+ }
+
+ int shift = log2_approx(maxval); //*(sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples)*2);
+
+#ifdef SL_DEBUG
+ LOG_I(PHY, "SIDELINK SLSS SEARCH: Function:%s\n", __func__);
+ LOG_I(PHY, "maxval:%d, shift:%d\n", maxval, shift);
+#endif
+
+ int64_t avg[SL_NR_NUM_IDs_IN_PSS] = {0};
+ int64_t peak_value = 0, psss_corr_value = 0;
+ unsigned int peak_position = 0, pss_source = 0;
+ for (int pss_index = 0; pss_index < SL_NR_NUM_IDs_IN_PSS; pss_index++)
+ avg[pss_index] = 0;
+
+#ifdef SL_DEBUG
+ int64_t *pss_corr_debug_values[SL_NR_NUM_IDs_IN_PSS];
+ for (int pss_index = 0; pss_index < SL_NR_NUM_IDs_IN_PSS; pss_index++)
+ pss_corr_debug_values[pss_index] = malloc16_clear(length * sizeof(int64_t));
+#endif
+
+ for (int n = 0; n < length - sl_fp->ofdm_symbol_size; n += 4) { //
+
+ for (int pss_index = 0; pss_index < SL_NR_NUM_IDs_IN_PSS; pss_index++) {
+ psss_corr_value = 0;
+
+ // calculate dot product of primary_synchro_time_nr and rxdata[ar][n] (ar=0..nb_ant_rx) and store the sum in temp[n];
+ for (int ar = 0; ar < sl_fp->nb_antennas_rx; ar++) {
+ /* perform correlation of rx data and pss sequence ie it is a dot product */
+ const c32_t result = dot_product((c16_t *)pss_for_correlation[pss_index],
+ (c16_t *)&(rxdata[ar][n + frame_index * sl_fp->samples_per_frame]),
+ sl_fp->ofdm_symbol_size,
+ shift);
+
+ const c64_t r64 = {.r = result.r, .i = result.i};
+ psss_corr_value += squaredMod(r64);
+
+#ifdef SL_DEBUG
+ pss_corr_debug_values[pss_index][n] = psss_corr_value;
+ printf("frame:%d n:%d, pss_index:%d, pss_for_correlation[pss_index][0]:%x, rxdata[n]:%x\n",
+ frame_index,
+ n,
+ pss_index,
+ pss_for_correlation[pss_index][0],
+ rxdata[ar][n + frame_index * sl_fp->samples_per_frame]);
+ printf("result %lld, pss_corr_values[%d][%d]:%ld\n", result, pss_index, n, pss_corr_debug_values[pss_index][n]);
+ printf("pss_index %d: n %6u peak_value %15llu\n", pss_index, n, (unsigned long long)pss_corr_debug_values[pss_index][n]);
+ printf("peak_value:%ld, peak_position:%d, pss_source:%d\n", peak_value, peak_position, pss_source);
+#endif
+ }
+
+ // calculate the absolute value of sync_corr[n]
+ avg[pss_index] += psss_corr_value;
+ if (psss_corr_value > peak_value) {
+ peak_value = psss_corr_value;
+ peak_position = n;
+ pss_source = pss_index;
+
+#ifdef SL_DEBUG
+ printf("pss_index %d: n %6u peak_value %15llu\n", pss_index, n, (unsigned long long)psss_corr_value);
+#endif
+ }
+ }
+ }
+
+#ifdef SL_DEBUG
+ LOG_M("pss_corr_debug_values_0.m", "pss_corr0", &pss_corr_debug_values[0][0], length, 1, 6);
+ LOG_M("pss_corr_debug_values_1.m", "pss_corr1", &pss_corr_debug_values[1][0], length, 1, 6);
+
+ for (int pss_index = 0; pss_index < SL_NR_NUM_IDs_IN_PSS; pss_index++) {
+ free(pss_corr_debug_values[pss_index]);
+ }
+#endif
+
+ double ffo_est = 0;
+ if (UE->UE_fo_compensation) { // Not tested
+
+ // fractional frequency offset computation according to Cross-correlation Synchronization Algorithm Using PSS
+ // Shoujun Huang, Yongtao Su, Ying He and Shan Tang, "Joint time and frequency offset estimation in LTE downlink," 7th
+ // International Conference on Communications and Networking in China, 2012.
+
+ c16_t *pss = (c16_t *)pss_for_correlation[pss_source];
+ c16_t *rxd = (c16_t *)&(rxdata[0][peak_position + frame_index * sl_fp->samples_per_frame]);
+ int half_symbol = sl_fp->ofdm_symbol_size >> 1;
+
+ // Computing cross-correlation at peak on half the symbol size for first half of data
+ c32_t r1 = dot_product(pss, rxd, half_symbol, shift);
+
+ // Computing cross-correlation at peak on half the symbol size for data shifted by half symbol size
+ // as it is real and complex it is necessary to shift by a value equal to symbol size to obtain such shift
+ c32_t r2 = dot_product(pss + half_symbol, rxd + half_symbol, half_symbol, shift);
+
+ cd_t r1d = {r1.r, r1.i}, r2d = {r2.r, r2.i};
+ // estimation of fractional frequency offset: angle[(result1)'*(result2)]/pi
+ ffo_est = atan2(r1d.r * r2d.i - r2d.r * r1d.i, r1d.r * r2d.r + r1d.i * r2d.i) / M_PI;
+
+#ifdef SL_DEBUG
+ printf("ffo %lf\n", ffo_est);
+#endif
+ }
+
+ // computing absolute value of frequency offset
+ sync_params->freq_offset = ffo_est * sl_fp->subcarrier_spacing;
+
+ for (int pss_index = 0; pss_index < SL_NR_NUM_IDs_IN_PSS; pss_index++)
+ avg[pss_index] /= (length / 4);
+
+ sync_params->N_sl_id2 = pss_source;
+
+ LOG_I(PHY,
+ "PSS Source = %d, Peak found at pos %d, val = %llu (%d dB) avg %d dB, ffo %lf, freq offset:%d Hz\n",
+ pss_source,
+ peak_position,
+ (unsigned long long)peak_value,
+ dB_fixed64(peak_value),
+ dB_fixed64(avg[pss_source]),
+ ffo_est,
+ sync_params->freq_offset);
+
+ if (peak_value < 5 * avg[pss_source])
+ return (-1);
+
+ return peak_position;
+}
+
+static void sl_nr_extract_sss(PHY_VARS_NR_UE *ue,
+ UE_nr_rxtx_proc_t *proc,
+ int32_t *tot_metric,
+ uint8_t *phase_max,
+ c16_t rxdataF[][ue->SL_UE_PHY_PARAMS.sl_frame_params.samples_per_slot_wCP])
+{
+ c16_t pss_ext[SL_NR_MAX_RX_ANTENNA][SL_NR_NUM_PSS_SYMBOLS][SL_NR_PSS_SEQUENCE_LENGTH];
+ c16_t sss_ext[SL_NR_MAX_RX_ANTENNA][SL_NR_NUM_SSS_SYMBOLS][SL_NR_PSS_SEQUENCE_LENGTH];
+ uint8_t Nid2 = ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id2;
+ NR_DL_FRAME_PARMS *sl_fp = &ue->SL_UE_PHY_PARAMS.sl_frame_params;
+ int16_t *d;
+ uint16_t Nid1 = 0;
+ uint8_t phase;
+ c16_t *rxF_ext;
+
+ for (int aarx = 0; aarx < sl_fp->nb_antennas_rx; aarx++) {
+ unsigned int ofdm_symbol_size = sl_fp->ofdm_symbol_size;
+
+ // pss, sss extraction
+ for (int sym = SL_NR_FIRST_PSS_SYMBOL; sym < SL_NR_FIRST_PSS_SYMBOL + SL_NR_NUM_PSS_SSS_SYMBOLS; sym++) {
+ if (sym < SL_NR_FIRST_PSS_SYMBOL + SL_NR_NUM_PSS_SYMBOLS) {
+ rxF_ext = &pss_ext[aarx][sym - SL_NR_FIRST_PSS_SYMBOL][0];
+ } else {
+ rxF_ext = &sss_ext[aarx][sym - SL_NR_FIRST_SSS_SYMBOL][0];
+ }
+
+ unsigned int k = sl_fp->first_carrier_offset + sl_fp->ssb_start_subcarrier + 2;
+ if (k >= ofdm_symbol_size)
+ k -= ofdm_symbol_size;
+
+ LOG_D(PHY,
+ "firstcarrieroffset:%d, ssb_sc:%d, k:%d, symbol:%d\n",
+ sl_fp->first_carrier_offset,
+ sl_fp->ssb_start_subcarrier,
+ k,
+ sym);
+
+ for (int i = 0; i < SL_NR_PSS_SEQUENCE_LENGTH; i++) {
+ rxF_ext[i] = rxdataF[aarx][sym * ofdm_symbol_size + k];
+ k++;
+ if (k == ofdm_symbol_size)
+ k = 0;
+ }
+ }
+
+ LOG_D(PHY, "SIDELINK SLSS SEARCH: EXTRACTION OF PSS, SSS done\n");
+
+#ifdef SL_DEBUG
+ LOG_M("pss_ext_sym1.m", "pss_ext1", &pss_ext[aarx][0][0], SL_NR_PSS_SEQUENCE_LENGTH, 1, 1);
+ LOG_M("pss_ext_sym2.m", "pss_ext2", &pss_ext[aarx][1][0], SL_NR_PSS_SEQUENCE_LENGTH, 1, 1);
+ LOG_M("sss_ext_sym3.m", "sss_ext3", &sss_ext[aarx][0][0], SL_NR_PSS_SEQUENCE_LENGTH, 1, 1);
+ LOG_M("sss_ext_sym4.m", "sss_ext4", &sss_ext[aarx][1][0], SL_NR_PSS_SEQUENCE_LENGTH, 1, 1);
+#endif
+
+ // get conjugated channel estimate from PSS, H* = R* \cdot PSS
+ // and do channel estimation and compensation based on PSS
+ int16_t *pss = ue->SL_UE_PHY_PARAMS.init_params.sl_pss_for_sync[Nid2];
+ c16_t *pss_ext2, *sss_ext2;
+
+ // 2 Symbols each for PSS and SSS
+ for (int j = 0; j < SL_NR_NUM_PSS_OR_SSS_SYMBOLS; j++) {
+ sss_ext2 = &sss_ext[aarx][j][0];
+ pss_ext2 = &pss_ext[aarx][j][0];
+
+ for (int i = 0; i < SL_NR_PSS_SEQUENCE_LENGTH; i++) {
+ // This is H*(PSS) = R* \cdot PSS
+ c16_t tmp = {.r = (pss_ext2[i].r * pss[i]), .i = (-pss_ext2[i].i * pss[i])};
+
+ int amp = c16amp2(tmp);
+ int shift = log2_approx(amp) / 2;
+
+ // This is R(SSS) \cdot H*(PSS)
+ c16_t tmp2 = c16mulShift(tmp, sss_ext2[i], shift);
+
+ // MRC on RX antennas
+ // sss_ext now contains the compensated SSS
+ if (aarx == 0) {
+ sss_ext2[i].r = tmp2.r;
+ sss_ext2[i].i = tmp2.i;
+ } else {
+ AssertFatal(1 == 0, "SIDELINK MORE THAN 1 RX ANTENNA NOT YET SUPPORTED\n");
+ }
+ }
+ }
+
+ LOG_D(PHY, "SIDELINK SLSS SEARCH: Ch. estimation SSS done\n");
+ }
+
+ /*
+
+ #ifdef SL_DEBUG
+
+ write_output("rxsig0.m","rxs0",&ue->common_vars.rxdata[0][0],ue->frame_parms.samples_per_subframe,1,1);
+ write_output("rxdataF0_pss.m","rxF0_pss",&ue->common_vars.rxdataF[0][0],frame_parms->ofdm_symbol_size,1,1);
+ write_output("rxdataF0_sss.m","rxF0_sss",&ue->common_vars.rxdataF[0][(SSS_SYMBOL_NB-PSS_SYMBOL_NB)*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1);
+ write_output("pss_ext.m","pss_ext",pss_ext,LENGTH_PSS_NR,1,1);
+
+ #endif
+ */
+
+ /* for phase evaluation, one uses an array of possible phase shifts */
+ /* then a correlation is done between received signal with a shift pĥase and the reference signal */
+ /* Computation of signal with shift phase is based on below formula */
+ /* cosinus cos(x + y) = cos(x)cos(y) - sin(x)sin(y) */
+ /* sinus sin(x + y) = sin(x)cos(y) + cos(x)sin(y) */
+
+ // now do the SSS detection based on the pre computed SSS sequences
+ *tot_metric = INT_MIN;
+ c16_t *sss = &sss_ext[0][0][0];
+
+ for (uint16_t id1 = 0; id1 < SL_NR_NUM_IDs_IN_SSS; id1++) { // all possible SSS Nid1 values
+ for (phase = 0; phase < PHASE_HYPOTHESIS_NUMBER; phase++) { // phase offset between PSS and SSS
+
+ int32_t metric = 0, metric_re = 0;
+
+ d = (int16_t *)&ue->SL_UE_PHY_PARAMS.init_params.sl_sss_for_sync[Nid2 * SL_NR_NUM_IDs_IN_SSS + id1];
+
+ // This is the inner product using one particular value of each unknown parameter
+ for (int i = 0; i < SL_NR_SSS_SEQUENCE_LENGTH; i++) {
+ metric_re += d[i] * (((int64_t)phase_nr[phase].r * sss[i].r - phase_nr[phase].i * sss[i].i) >> 15);
+ }
+
+ metric = metric_re;
+
+ // if the current metric is better than the last save it
+ if (metric > *tot_metric) {
+ *tot_metric = metric;
+ Nid1 = id1;
+ *phase_max = phase;
+
+ LOG_D(PHY,
+ "(phase,Nid1) (%d,%d), metric_phase %d tot_metric %d, phase_max %d \n",
+ phase,
+ Nid1,
+ metric,
+ *tot_metric,
+ *phase_max);
+ }
+ }
+ }
+
+ ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id1 = Nid1;
+ ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id =
+ ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id1 + SL_NR_NUM_IDs_IN_SSS * ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id2;
+ LOG_I(PHY,
+ "UE[%d]NR-SL SLSS SEARCH: SSS Processing over. id2 from SSS:%d, id1 from PSS:%d, SLSS id:%d\n",
+ ue->Mod_id,
+ ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id1,
+ ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id2,
+ ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id);
+
+#ifdef SL_DEBUG
+#define SSS_METRIC_FLOOR_NR (30000)
+ if (*tot_metric > SSS_METRIC_FLOOR_NR) {
+ Nid2 = ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id2;
+ Nid1 = ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id1;
+ printf("Nid2 %d Nid1 %d tot_metric %d, phase_max %d \n", Nid2, Nid1, *tot_metric, *phase_max);
+ }
+#endif
+
+ return;
+}
+
+// Right now 2 frames worth of samples get processed for PSS in OAI.
+// For PSS in Sidelink, worst case 1 SSB in 16 frames can be present
+// Hence 16 frames worth of samples needs to be correlated to find the PSS.
+nr_initial_sync_t sl_nr_slss_search(PHY_VARS_NR_UE *UE, UE_nr_rxtx_proc_t *proc, int num_frames)
+{
+ sl_nr_ue_phy_params_t *sl_ue = &UE->SL_UE_PHY_PARAMS;
+ SL_NR_SYNC_PARAMS_t *sync_params = &sl_ue->sync_params;
+ NR_DL_FRAME_PARMS *sl_fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params;
+
+ int32_t sync_pos = -1; // sync_pos_frame = -1;
+ int32_t metric_tdd_ncp = 0;
+ uint8_t phase_tdd_ncp;
+ double im, re;
+ int ret = -1;
+ uint16_t rx_slss_id = 65535;
+
+ nr_initial_sync_t result = {true, 0};
+
+#ifdef SL_DEBUG_SEARCH_SLSS
+ LOG_D(PHY, "SIDELINK SEARCH SLSS: Function:%s\n", __func__);
+#endif
+
+ /* Initial synchronisation
+ *
+ * 1 radio frame = 10 ms
+ * <--------------------------------------------------------------------------->
+ * | Received UE data buffer |
+ * ----------------------------------------------------------------------------
+ * <-------------->|psbch|pss|pss|sss|sss|psbch sym5-sym 12|sym13 - guard|
+ * sync_pos SS/PSBCH block
+ */
+
+ // initial sync performed on 16 successive frames. Worst case - one PSBCH can be sent in 16 frames.
+ // If psbch passes on first frame, no need to process second frame
+ // Problem with the frame approach is that
+ // --------- SSB can be on the boundary between frames. In this case if only 1 SSB is sent we will miss it.
+ // rxdata will hold 16 frames + slot worth of samples. This needs to be processed to find the best SSB
+ for (int frame_index = 0; frame_index < num_frames; frame_index++) {
+ /* process pss search on received buffer */
+ sync_pos = sl_nr_pss_correlation(UE, frame_index);
+
+ if (sync_pos == -1) {
+ LOG_I(PHY, "SIDELINK SEARCH SLSS: No PSSS found in this frame\n");
+ continue;
+ }
+
+ sync_pos += frame_index * sl_fp->samples_per_frame; // position in the num_frames frame samples
+
+ for (int pss_sym = 1; pss_sym < 3; pss_sym++) {
+ // Now Sync pos can point to PSS 1st symbol or 2nd symbol.
+ // Right now implemented the strategy to try both locations for FFT
+ // Think about a better correlation strategy
+ if (pss_sym == 1) { // Check if sync pos points to SYMBOL1 - first symbol of PSS location
+ if (sync_pos > sl_fp->nb_prefix_samples0 + sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples)
+ sync_params->ssb_offset = sync_pos - (sl_fp->nb_prefix_samples0 + sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples);
+ else
+ sync_params->ssb_offset = sync_pos + sl_fp->samples_per_frame
+ - (sl_fp->nb_prefix_samples0 + sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples);
+ } else { // Check if sync pos points to SYMBOL2 - second symbol of PSS location
+ if (sync_pos >= sl_fp->nb_prefix_samples0 + 2 * (sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples))
+ sync_params->ssb_offset =
+ sync_pos - (sl_fp->nb_prefix_samples0 + 2 * (sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples));
+ else
+ sync_params->ssb_offset = sync_pos + sl_fp->samples_per_frame
+ - (sl_fp->nb_prefix_samples0 + 2 * (sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples));
+ }
+
+ LOG_I(PHY,
+ "UE[%d]SIDELINK SEARCH SLSS: PSS Peak at %d, PSS sym:%d, Estimated PSS position %d\n",
+ UE->Mod_id,
+ sync_pos,
+ pss_sym,
+ sync_params->ssb_offset);
+
+ int slss_block_samples = (SL_NR_NUMSYM_SLSS_NORMAL_CP * sl_fp->ofdm_symbol_size)
+ + (SL_NR_NUMSYM_SLSS_NORMAL_CP - 1) * sl_fp->nb_prefix_samples + sl_fp->nb_prefix_samples0;
+
+ int ssb_end_position = sync_params->ssb_offset + slss_block_samples;
+
+ LOG_D(PHY,
+ "ssb_end:%d ssb block samples:%d total samples: %d\n",
+ ssb_end_position,
+ slss_block_samples,
+ num_frames * sl_fp->samples_per_frame);
+
+ /* check that SSS/PBCH block is continuous inside the received buffer */
+ if (ssb_end_position < num_frames * sl_fp->samples_per_frame) {
+ // digital compensation of FFO for SSB symbols
+ if (UE->UE_fo_compensation) { // This code to be checked. Why do we do this before PSS detection is successful?
+ double s_time = 1 / (1.0e3 * sl_fp->samples_per_subframe); // sampling time
+ double off_angle = -2 * M_PI * s_time * (sync_params->freq_offset); // offset rotation angle compensation per sample
+
+ int start = sync_params->ssb_offset; // start for offset correction is at ssb_offset (pss time position)
+ // Adapt this for other numerologies number of symbols with larger cp increases TBD
+ int end = ssb_end_position; // loop over samples in all symbols (ssb size), including prefix
+
+ LOG_I(PHY,
+ "SLSS SEARCH: FREQ comp of SLSS samples. Freq_OFSET:%d, startpos:%d, end_pos:%d\n",
+ sync_params->freq_offset,
+ start,
+ end);
+ for (int n = start; n < end; n++) {
+ for (int ar = 0; ar < sl_fp->nb_antennas_rx; ar++) {
+ re = ((double)(((short *)UE->common_vars.rxdata[ar]))[2 * n]);
+ im = ((double)(((short *)UE->common_vars.rxdata[ar]))[2 * n + 1]);
+ ((short *)UE->common_vars.rxdata[ar])[2 * n] = (short)(round(re * cos(n * off_angle) - im * sin(n * off_angle)));
+ ((short *)UE->common_vars.rxdata[ar])[2 * n + 1] = (short)(round(re * sin(n * off_angle) + im * cos(n * off_angle)));
+ }
+ }
+ }
+
+ NR_DL_FRAME_PARMS *frame_parms = &UE->SL_UE_PHY_PARAMS.sl_frame_params;
+ const uint32_t rxdataF_sz = frame_parms->samples_per_slot_wCP;
+ __attribute__((aligned(32))) c16_t rxdataF[frame_parms->nb_antennas_rx][rxdataF_sz];
+
+ /* In order to achieve correct processing for NR prefix samples is forced to 0 and then restored after function call */
+ for (int symbol = 0; symbol < SL_NR_NUMSYM_SLSS_NORMAL_CP; symbol++) {
+ sl_nr_slot_fep(UE, NULL, symbol, 0, sync_params->ssb_offset, rxdataF);
+ }
+
+ sl_nr_extract_sss(UE, NULL, &metric_tdd_ncp, &phase_tdd_ncp, rxdataF);
+
+ // save detected cell id to psbch
+ rx_slss_id = UE->SL_UE_PHY_PARAMS.sync_params.N_sl_id;
+
+ __attribute__((aligned(32))) struct complex16 dl_ch_estimates[frame_parms->nb_antennas_rx][rxdataF_sz];
+ __attribute__((
+ aligned(32))) struct complex16 dl_ch_estimates_time[frame_parms->nb_antennas_rx][frame_parms->ofdm_symbol_size];
+
+ uint8_t decoded_output[4];
+
+ for (int symbol = 0; symbol < SL_NR_NUMSYM_SLSS_NORMAL_CP - 1;) {
+ nr_pbch_channel_estimation(UE,
+ frame_parms,
+ rxdataF_sz,
+ dl_ch_estimates,
+ dl_ch_estimates_time,
+ proc,
+ symbol,
+ symbol,
+ 0,
+ 0,
+ rxdataF,
+ 1,
+ rx_slss_id);
+
+ symbol = (symbol == 0) ? 5 : symbol + 1;
+ }
+
+ ret = nr_rx_psbch(UE, proc, rxdataF_sz, dl_ch_estimates, frame_parms, decoded_output, rxdataF, rx_slss_id);
+
+ result.cell_detected = (ret == 0) ? true : false;
+
+ if (result.cell_detected) { // Check this later TBD
+ // sync at symbol ue->symbol_offset
+ // computing the offset wrt the beginning of the frame
+ // SSB located at symbol 0
+ sync_params->remaining_frames =
+ (num_frames * sl_fp->samples_per_frame - sync_params->ssb_offset) / sl_fp->samples_per_frame;
+ // ssb_offset points to start of sl-ssb
+ // rx_offset points to remaining samples needed to fill a frame
+ sync_params->rx_offset = sync_params->ssb_offset % sl_fp->samples_per_frame;
+
+ LOG_I(PHY,
+ "UE[%d]SIDELINK SLSS SEARCH: PSBCH RX OK. Remainingframes:%d, rx_offset:%d\n",
+ UE->Mod_id,
+ sync_params->remaining_frames,
+ sync_params->rx_offset);
+
+ uint32_t psbch_payload = (*(uint32_t *)decoded_output);
+ // retrieve DFN and slot number from SL-MIB
+ sync_params->DFN = (((psbch_payload & 0x0700) >> 1) | ((psbch_payload & 0xFE0000) >> 17));
+ sync_params->slot_offset = (((psbch_payload & 0x010000) >> 10) | ((psbch_payload & 0xFC000000) >> 26));
+
+ LOG_I(PHY,
+ "UE[%d]SIDELINK SLSS SEARCH: SL-MIB: DFN:%d, slot:%d.\n",
+ UE->Mod_id,
+ sync_params->DFN,
+ sync_params->slot_offset);
+
+ nr_sl_psbch_rsrp_measurements(sl_ue, frame_parms, rxdataF, false);
+
+ UE->init_sync_frame = sync_params->remaining_frames;
+ result.rx_offset = sync_params->rx_offset;
+
+ nr_sidelink_indication_t sl_indication;
+ sl_nr_rx_indication_t rx_ind = {0};
+ uint16_t number_pdus = 1;
+ nr_fill_sl_indication(&sl_indication, &rx_ind, NULL, proc, UE, NULL);
+ nr_fill_sl_rx_indication(&rx_ind, SL_NR_RX_PDU_TYPE_SSB, UE, number_pdus, proc, (void *)decoded_output, rx_slss_id);
+
+ LOG_D(PHY, "Sidelink SLSS SEARCH PSBCH RX OK. Send SL-SSB TO MAC\n");
+
+ if (UE->if_inst && UE->if_inst->sl_indication)
+ UE->if_inst->sl_indication(&sl_indication);
+
+ break;
+ }
+
+ LOG_I(PHY,
+ "SIDELINK SLSS SEARCH: SLSS ID: %d metric %d, phase %d, psbch CRC %s\n",
+ sl_ue->sync_params.N_sl_id,
+ metric_tdd_ncp,
+ phase_tdd_ncp,
+ (ret == 0) ? "OK" : "NOT OK");
+
+ } else {
+ LOG_W(PHY, "SIDELINK SLSS SEARCH: Error: Not enough samples to process PSBCH. sync_pos %d\n", sync_pos);
+ }
+ }
+ if (result.cell_detected)
+ break;
+ }
+
+ if (!result.cell_detected) { // PSBCH not found so indicate sync to higher layers and configure frame parameters
+ LOG_E(PHY, "SIDELINK SLSS SEARCH: PSBCH not received. Estimated PSS position:%d\n", sync_pos);
+ }
+
+ return result;
+}
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c
index ba5590d9f6..f60d9c89ea 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c
@@ -43,7 +43,6 @@
//#define DEBUG_PBCH_ENCODING
#define PBCH_A 24
-#define PBCH_MAX_RE_PER_SYMBOL (20*12)
#define PBCH_MAX_RE (PBCH_MAX_RE_PER_SYMBOL*4)
#define print_shorts(s,x) printf("%s : %d,%d,%d,%d,%d,%d,%d,%d\n",s,((int16_t*)x)[0],((int16_t*)x)[1],((int16_t*)x)[2],((int16_t*)x)[3],((int16_t*)x)[4],((int16_t*)x)[5],((int16_t*)x)[6],((int16_t*)x)[7])
@@ -241,20 +240,19 @@ int nr_pbch_channel_level(struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER
return(avg2);
}
-static void nr_pbch_channel_compensation(struct complex16 rxdataF_ext[][PBCH_MAX_RE_PER_SYMBOL],
- struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL],
- int nb_re,
- struct complex16 rxdataF_comp[][PBCH_MAX_RE_PER_SYMBOL],
- NR_DL_FRAME_PARMS *frame_parms,
- uint8_t output_shift) {
+void nr_pbch_channel_compensation(struct complex16 rxdataF_ext[][PBCH_MAX_RE_PER_SYMBOL],
+ struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL],
+ int nb_re,
+ struct complex16 rxdataF_comp[][PBCH_MAX_RE_PER_SYMBOL],
+ NR_DL_FRAME_PARMS *frame_parms,
+ uint8_t output_shift)
+{
for (int aarx=0; aarxnb_antennas_rx; aarx++) {
simde__m128i *dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[aarx];
simde__m128i *rxdataF128 = (simde__m128i *)rxdataF_ext[aarx];
simde__m128i *rxdataF_comp128 = (simde__m128i *)rxdataF_comp[aarx];
- for (int re=0; re31)
pbch_llr8[i]=32;
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c b/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c
new file mode 100644
index 0000000000..cf8ea994f1
--- /dev/null
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c
@@ -0,0 +1,239 @@
+/*
+ * 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.0 (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.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
+ */
+
+#include "PHY/defs_nr_UE.h"
+#include "PHY/CODING/nrPolar_tools/nr_polar_psbch_defs.h"
+#include "PHY/CODING/nrPolar_tools/nr_polar_defs.h"
+#include "common/utils/LOG/log.h"
+#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
+
+// #define DEBUG_PSBCH
+
+static void nr_psbch_extract(uint32_t rxdataF_sz,
+ c16_t rxdataF[][rxdataF_sz],
+ int estimateSz,
+ struct complex16 dl_ch_estimates[][estimateSz],
+ struct complex16 rxdataF_ext[][SL_NR_NUM_PSBCH_DATA_RE_IN_ONE_SYMBOL],
+ struct complex16 dl_ch_estimates_ext[][SL_NR_NUM_PSBCH_DATA_RE_IN_ONE_SYMBOL],
+ uint32_t symbol,
+ NR_DL_FRAME_PARMS *frame_params)
+{
+ uint16_t rb;
+ uint8_t i, j, aarx;
+ struct complex16 *dl_ch0, *dl_ch0_ext, *rxF, *rxF_ext;
+ const uint8_t nb_rb = SL_NR_NUM_PSBCH_RBS_IN_ONE_SYMBOL;
+
+ AssertFatal((symbol == 0 || symbol >= 5), "SIDELINK: PSBCH DMRS not contained in symbol %d \n", symbol);
+
+ for (aarx = 0; aarx < frame_params->nb_antennas_rx; aarx++) {
+ unsigned int rx_offset = frame_params->first_carrier_offset + frame_params->ssb_start_subcarrier;
+ rx_offset = rx_offset % frame_params->ofdm_symbol_size;
+
+ rxF = &rxdataF[aarx][symbol * frame_params->ofdm_symbol_size];
+ rxF_ext = &rxdataF_ext[aarx][0];
+
+ dl_ch0 = &dl_ch_estimates[aarx][symbol * frame_params->ofdm_symbol_size];
+ dl_ch0_ext = &dl_ch_estimates_ext[aarx][0];
+
+#ifdef DEBUG_PSBCH
+ LOG_I(PHY, "extract_rbs: rx_offset=%d, symbol %u\n", (rx_offset + (symbol * frame_params->ofdm_symbol_size)), symbol);
+#endif
+
+ for (rb = 0; rb < nb_rb; rb++) {
+ j = 0;
+
+ for (i = 0; i < NR_NB_SC_PER_RB; i++) {
+ if (i % 4 != 0) {
+ rxF_ext[j] = rxF[rx_offset];
+ dl_ch0_ext[j] = dl_ch0[i];
+
+#ifdef DEBUG_PSBCH
+
+ LOG_I(PHY,
+ "rxF ext[%d] = (%d,%d) rxF [%u]= (%d,%d)\n",
+ (9 * rb) + j,
+ rxF_ext[j].r,
+ rxF_ext[j].i,
+ rx_offset,
+ rxF[rx_offset].r,
+ rxF[rx_offset].i);
+ LOG_I(PHY,
+ "dl ch0 ext[%d] = (%d,%d) dl_ch0 [%d]= (%d,%d)\n",
+ (9 * rb) + j,
+ dl_ch0_ext[j].r,
+ dl_ch0_ext[j].i,
+ i,
+ dl_ch0[i].r,
+ dl_ch0[i].i);
+#endif
+ j++;
+ }
+ rx_offset = (rx_offset + 1) % (frame_params->ofdm_symbol_size);
+ }
+
+ rxF_ext += SL_NR_NUM_PSBCH_DATA_RE_IN_ONE_RB;
+ dl_ch0_ext += SL_NR_NUM_PSBCH_DATA_RE_IN_ONE_RB;
+ dl_ch0 += NR_NB_SC_PER_RB;
+ }
+
+#ifdef DEBUG_PSBCH
+ char filename[40], varname[25];
+ sprintf(filename, "psbch_dlch_sym_%d.m", symbol);
+ sprintf(varname, "psbch_dlch%d.m", symbol);
+ LOG_M(filename, varname, (void *)dl_ch0, frame_params->ofdm_symbol_size, 1, 1);
+ sprintf(filename, "psbch_dlchext_sym_%d.m", symbol);
+ sprintf(varname, "psbch_dlchext%d.m", symbol);
+ LOG_M(filename, varname, (void *)&dl_ch_estimates_ext[0][0], SL_NR_NUM_PSBCH_DATA_RE_IN_ONE_SYMBOL, 1, 1);
+#endif
+ }
+
+ return;
+}
+
+int nr_rx_psbch(PHY_VARS_NR_UE *ue,
+ UE_nr_rxtx_proc_t *proc,
+ int estimateSz,
+ struct complex16 dl_ch_estimates[][estimateSz],
+ NR_DL_FRAME_PARMS *frame_parms,
+ uint8_t *decoded_output,
+ c16_t rxdataF[][frame_parms->samples_per_slot_wCP],
+ uint16_t slss_id)
+{
+ uint32_t decoderState = 0;
+ int psbch_e_rx_idx = 0;
+ // Extra 2 bits needed as polar decoder expects a multiple of 4 as encoder length
+ // If these 2 bits are not added, runs compiled with --sanitize will fail.
+ int16_t psbch_e_rx[SL_NR_POLAR_PSBCH_E_NORMAL_CP + 2] = {0};
+
+#ifdef DEBUG_PSBCH
+ write_output("psbch_rxdataF.m",
+ "psbchrxF",
+ &rxdataF[0][0],
+ frame_parms->ofdm_symbol_size * SL_NR_NUM_SYMBOLS_SSB_NORMAL_CP,
+ 1,
+ 1);
+#endif
+ // symbol refers to symbol within SSB. symbol_offset is the offset of the SSB wrt start of slot
+ double log2_maxh = 0;
+
+ // 0 for Normal Cyclic Prefix and 1 for EXT CyclicPrefix
+ const int numsym = (frame_parms->Ncp) ? SL_NR_NUM_SYMBOLS_SSB_EXT_CP : SL_NR_NUM_SYMBOLS_SSB_NORMAL_CP;
+
+ for (int symbol = 0; symbol < numsym;) {
+ const uint16_t nb_re = SL_NR_NUM_PSBCH_DATA_RE_IN_ONE_SYMBOL;
+ __attribute__((aligned(32))) struct complex16 rxdataF_ext[frame_parms->nb_antennas_rx][nb_re + 1];
+ __attribute__((aligned(32))) struct complex16 dl_ch_estimates_ext[frame_parms->nb_antennas_rx][nb_re + 1];
+ // memset(dl_ch_estimates_ext,0, sizeof dl_ch_estimates_ext);
+ nr_psbch_extract(frame_parms->samples_per_slot_wCP,
+ rxdataF,
+ estimateSz,
+ dl_ch_estimates,
+ rxdataF_ext,
+ dl_ch_estimates_ext,
+ symbol,
+ frame_parms);
+#ifdef DEBUG_PSBCH
+ LOG_I(PHY, "PSBCH RX Symbol %d ofdm size %d\n", symbol, frame_parms->ofdm_symbol_size);
+#endif
+
+ int max_h = 0;
+ if (symbol == 0) {
+ max_h = nr_pbch_channel_level(dl_ch_estimates_ext, frame_parms, nb_re);
+ // log2_maxh = 3+(log2_approx(max_h)/2);
+ log2_maxh = 5 + (log2_approx(max_h) / 2); // LLR32 crc error. LLR 16 CRC works
+ }
+#ifdef DEBUG_PSBCH
+ LOG_I(PHY, "PSBCH RX log2_maxh = %f (%d)\n", log2_maxh, max_h);
+#endif
+
+ __attribute__((aligned(32))) struct complex16 rxdataF_comp[frame_parms->nb_antennas_rx][nb_re + 1];
+ nr_pbch_channel_compensation(rxdataF_ext,
+ dl_ch_estimates_ext,
+ nb_re,
+ rxdataF_comp,
+ frame_parms,
+ log2_maxh); // log2_maxh+I0_shift
+
+ nr_pbch_quantize(psbch_e_rx + psbch_e_rx_idx, (short *)rxdataF_comp[0], SL_NR_NUM_PSBCH_DATA_BITS_IN_ONE_SYMBOL);
+
+ psbch_e_rx_idx += SL_NR_NUM_PSBCH_DATA_BITS_IN_ONE_SYMBOL;
+
+ // SKIP 2 SL-PSS AND 2 SL-SSS symbols
+ // Symbols carrying PSBCH 0, 5-12
+ symbol = (symbol == 0) ? 5 : symbol + 1;
+ }
+
+#if 0 // ENABLE SCOPE LATER
+ UEscopeCopy(ue, psbchRxdataF_comp, psbch_unClipped, sizeof(struct complex16), frame_parms->nb_antennas_rx, psbch_e_rx_idx/2);
+ UEscopeCopy(ue, psbchLlr, psbch_e_rx, sizeof(int16_t), frame_parms->nb_antennas_rx, psbch_e_rx_idx);
+#endif
+
+#ifdef DEBUG_PSBCH
+ write_output("psbch_rxdataFcomp.m", "psbch_rxFcomp", psbch_unClipped, SL_NR_NUM_PSBCH_DATA_RE_IN_ALL_SYMBOLS, 1, 1);
+#endif
+
+ // un-scrambling
+ LOG_D(PHY, "PSBCH RX POLAR DECODING: total PSBCH bits:%d, rx_slss_id:%d\n", psbch_e_rx_idx, slss_id);
+
+ nr_pbch_unscrambling(psbch_e_rx, slss_id, 0, 0, psbch_e_rx_idx, 0, 0, 0, NULL);
+ // polar decoding de-rate matching
+ uint64_t tmp = 0;
+ decoderState = polar_decoder_int16(psbch_e_rx,
+ (uint64_t *)&tmp,
+ 0,
+ SL_NR_POLAR_PSBCH_MESSAGE_TYPE,
+ SL_NR_POLAR_PSBCH_PAYLOAD_BITS,
+ SL_NR_POLAR_PSBCH_AGGREGATION_LEVEL);
+
+ uint32_t psbch_payload = tmp;
+
+ if (decoderState) {
+ LOG_D(PHY, "%d:%d PSBCH RX: NOK \n", proc->frame_rx, proc->nr_slot_rx);
+ return (decoderState);
+ }
+
+ // Decoder reversal
+ uint32_t a_reversed = 0;
+
+ for (int i = 0; i < SL_NR_POLAR_PSBCH_PAYLOAD_BITS; i++)
+ a_reversed |= (((uint64_t)psbch_payload >> i) & 1) << (31 - i);
+
+ psbch_payload = a_reversed;
+
+ *((uint32_t *)decoded_output) = psbch_payload;
+
+#ifdef DEBUG_PSBCH
+ for (int i = 0; i < 4; i++) {
+ LOG_I(PHY, "decoded_output[%d]:%x\n", i, decoded_output[i]);
+ }
+#endif
+
+ ue->symbol_offset = 0;
+
+ // retrieve DFN and slot number from SL-MIB
+ uint32_t DFN = 0, slot_offset = 0;
+ DFN = (((psbch_payload & 0x0700) >> 1) | ((psbch_payload & 0xFE0000) >> 17));
+ slot_offset = (((psbch_payload & 0x010000) >> 10) | ((psbch_payload & 0xFC000000) >> 26));
+
+ LOG_D(PHY, "PSBCH RX SL-MIB:%x, decoded DFN:slot %d:%d, %x\n", psbch_payload, DFN, slot_offset, *(uint32_t *)decoded_output);
+
+ return 0;
+}
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_tx.c b/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_tx.c
new file mode 100644
index 0000000000..cc8993bc6d
--- /dev/null
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_tx.c
@@ -0,0 +1,384 @@
+/*
+ * 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
+ */
+
+#include "PHY/defs_nr_UE.h"
+#include "PHY/LTE_REFSIG/lte_refsig.h"
+#include "PHY/NR_REFSIG/nr_mod_table.h"
+#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
+#include "PHY/CODING/nrPolar_tools/nr_polar_psbch_defs.h"
+#include "PHY/MODULATION/nr_modulation.h"
+
+// #define SL_DEBUG
+
+/**
+ *This function performs PSBCH SCrambling as described in 38.211.
+ *Input parameter "output" is scrambled and the scrambled output is stored in this parameter.
+ *id - SLSS ID used for C_INIT
+ *length is the length of the buffer.
+ */
+void sl_psbch_scrambling(uint32_t *output, uint32_t id, uint16_t length)
+{
+ uint32_t x1, x2, s = 0;
+ // x1 is set in lte_gold_generic
+ x2 = id; // C_INIT
+
+#ifdef SL_DEBUG
+ printf("SIDELINK: Function %s\n", __func__);
+ printf("Scrambling params: length %d id %d \n", length, id);
+#endif
+
+#ifdef SL_DEBUG
+ for (int i = 0; i < 56; i++) {
+ printf("\nBEFORE SCRAMBLING output[%d]:0x%x\n", i, output[i]);
+ }
+#endif
+
+ // get initial 32 scrambing bits
+ s = lte_gold_generic(&x1, &x2, 1);
+#ifdef SL_DEBUG
+ printf("s: %04x\t", s);
+#endif
+
+ // scramble in 32bit chunks
+ int i = 0;
+ while (i + 32 <= length) {
+ output[i >> 5] ^= s;
+
+ i += 32;
+ s = lte_gold_generic(&x1, &x2, 0);
+#ifdef SL_DEBUG
+ printf("s: %04x\t", s);
+#endif
+ }
+
+ // scramble remaining bits
+ for (; i < length; ++i) {
+ output[i >> 5] ^= ((s >> (i & 0x1f) & 1) << (i & 0x1f));
+ }
+
+#ifdef SL_DEBUG
+ for (int i = 0; i < 56; i++) {
+ printf("\nAFTER SCRAMBLING output[%d]:0x%x\n", i, output[i]);
+ }
+#endif
+}
+
+/**
+ *This function RE MAPS PSS, SSS sequences as described in 38.211.
+ *txF is the data in frequency domain, sync_seq = PSS or SSS seq
+ *startsym = 1 for PSS, 3 for SSS
+ *re_offset = sample which points to first RE + SSB start RE
+ *scaling factor = scaling factor used for PSS, SSS (determined according to PSBCH pwr)
+ *symbol size = OFDM symbol size used for RE Mapping
+ */
+void sl_map_pss_or_sss(c16_t *txF,
+ int16_t *sync_seq,
+ uint16_t startsym,
+ uint16_t re_offset,
+ uint16_t scaling_factor,
+ uint16_t symbol_size)
+{
+#ifdef SL_DEBUG
+ printf("%s. DEBUG PSBCH TX: RE MAPPING of PSS/SSS \n", __func__);
+ printf("Input Params - StartSYM:%d, NUMSYM:%d, RE_OFFSET:%d, num_REs:%d, scaling_factor:%d, symbol_size:%d\n",
+ startsym,
+ SL_NR_NUM_PSS_OR_SSS_SYMBOLS,
+ re_offset,
+ SL_NR_NUM_PSBCH_RE_IN_ONE_SYMBOL,
+ scaling_factor,
+ symbol_size);
+#endif
+
+ // RE Mapping of SL-PSS, SL-SSS
+ for (int l = startsym; l < (startsym + SL_NR_NUM_PSS_OR_SSS_SYMBOLS); l++) {
+ int k = re_offset % symbol_size;
+ int index = 0, offset = 0;
+
+ for (int m = 0; m < SL_NR_NUM_PSBCH_RE_IN_ONE_SYMBOL; m++) {
+ offset = l * symbol_size + k;
+ if ((m < 2) || (m >= (SL_NR_NUM_PSBCH_RE_IN_ONE_SYMBOL - 3))) {
+ txF[offset].r = 0; // Set REs 0,1,129,130,131 = 0
+
+#ifdef SL_DEBUG
+ printf("sym:%d, RE:%d, txF[%d]:%d.%d \n", l, m, offset, txF[offset].r, txF[offset].i);
+#endif
+
+ } else {
+ txF[offset].r = (sync_seq[index] * scaling_factor) >> 15;
+
+#ifdef SL_DEBUG
+ printf("sym:%d, RE:%d, txF[%d]:%d.%d, syncseq[%d]:%d \n",
+ l,
+ m,
+ offset,
+ txF[offset].r,
+ txF[offset].i,
+ index,
+ sync_seq[index]);
+#endif
+
+ index++;
+ }
+ txF[offset].i = 0;
+ k = (k + 1) % symbol_size;
+ }
+ }
+}
+
+/**
+ * This function Generates the PSBCH DATA Modulation symbols and RE MAPS PSBCH Modulated symbols
+ * and PSBCH DMRS sequences as described in 38.211.
+ * txF is the data in frequency domain
+ * payload is the PSBCH payload (SL-MIB given by higher layers)
+ * id - SLSS ID used for knowing which DMRS sequence to be used.
+ * Cp - NORMAL of extended Cyclic prefix
+ * startsym = 0 and then PSBCH is mapped from symbols 5-13 if normal , 5-11 if extended
+ * re_offset = sample which points to first RE + SSB start RE
+ * scaling factor = scaling factor used for PSS, SSS (determined according to PSBCH pwr)
+ * symbol size = OFDM symbol size used for RE Mapping
+ */
+
+void sl_generate_and_map_psbch(c16_t *txF,
+ uint32_t *payload,
+ uint16_t id,
+ uint16_t cp,
+ uint16_t re_offset,
+ uint16_t scaling_factor,
+ uint16_t symbol_size,
+ c16_t *psbch_dmrs)
+{
+ uint64_t psbch_a_reversed = 0;
+ uint16_t num_psbch_modsym = 0, numsym = 0;
+ const int mod_order = 2; // QPSK
+ uint32_t encoder_output[SL_NR_POLAR_PSBCH_E_DWORD];
+ struct complex16 psbch_modsym[SL_NR_NUM_PSBCH_MODULATED_SYMBOLS];
+
+ LOG_D(PHY, "PSBCH TX: Generation accg to 38.212, 38.211. SLSS id:%d\n", id);
+
+ // Encoder reversal
+ for (int i = 0; i < SL_NR_POLAR_PSBCH_PAYLOAD_BITS; i++)
+ psbch_a_reversed |= (((uint64_t)*payload >> i) & 1) << (31 - i);
+
+#ifdef SL_DEBUG
+ printf("DEBUG PSBCH TX: 38.212 PSBCH CRC + Channel coding (POLAR) + Rate Matching:\n");
+ printf("PSBCH payload:%x, Reversed Payload:%016lx\n", *payload, psbch_a_reversed);
+#endif
+
+ /// CRC, coding and rate matching
+ polar_encoder_fast(&psbch_a_reversed,
+ (void *)encoder_output,
+ 0,
+ 0,
+ SL_NR_POLAR_PSBCH_MESSAGE_TYPE,
+ SL_NR_POLAR_PSBCH_PAYLOAD_BITS,
+ SL_NR_POLAR_PSBCH_AGGREGATION_LEVEL);
+
+#ifdef SL_DEBUG
+ for (int i = 0; i < SL_NR_POLAR_PSBCH_E_DWORD; i++)
+ printf("encoderoutput[%d]: 0x%08x\t", i, encoder_output[i]);
+ printf("\n");
+#endif
+
+ /// 38.211 Scrambling
+ if (cp) { // EXT Cyclic prefix
+ sl_psbch_scrambling(encoder_output, id, SL_NR_POLAR_PSBCH_E_EXT_CP); // for Extended Cyclic prefix
+ num_psbch_modsym = SL_NR_POLAR_PSBCH_E_EXT_CP / mod_order;
+ numsym = SL_NR_NUM_SYMBOLS_SSB_EXT_CP;
+ AssertFatal(1 == 0, "EXT CP is not yet supported\n");
+ } else { // Normal CP
+ sl_psbch_scrambling(encoder_output, id, SL_NR_POLAR_PSBCH_E_NORMAL_CP); // for Cyclic prefix
+ num_psbch_modsym = SL_NR_POLAR_PSBCH_E_NORMAL_CP / mod_order;
+ numsym = SL_NR_NUM_SYMBOLS_SSB_NORMAL_CP;
+ }
+
+ LOG_D(PHY, "PSBCH TX: 38.211 Scrambling done. Number of bits:%d \n", SL_NR_POLAR_PSBCH_E_NORMAL_CP);
+
+#ifdef SL_DEBUG
+ printf("38211 STEP: PSBCH Scrambling \n");
+ for (int i = 0; i < SL_NR_POLAR_PSBCH_E_NORMAL_CP / 32; i++)
+ printf("Scrambleroutput[%d]: 0x%08x\t", i, encoder_output[i]);
+ printf("\n");
+#endif
+
+#ifdef SL_DEBUG
+ printf("SIDELINK PSBCH TX: 38211 STEP: QPSK Modulation of PSBCH symbols:%d, symbols in PSBCH:%d\n", num_psbch_modsym, numsym);
+#endif
+
+ /// 38.211 QPSK modulation
+ nr_modulation(encoder_output, num_psbch_modsym * mod_order, mod_order, (int16_t *)psbch_modsym);
+
+ // RE MApping of PSBCH and PSBCH DMRS
+ int index = 0, dmrs_index = 0;
+ const int numre = SL_NR_NUM_PSBCH_RE_IN_ONE_SYMBOL;
+
+#ifdef SL_DEBUG
+ LOG_M("sl_psbch_data_symbols.m", "psbch_sym", (void *)psbch_modsym, num_psbch_modsym, 1, 1);
+ LOG_M("sl_psbch_dmrs_symbols.m", "psbch_dmrs", (void *)psbch_dmrs, SL_NR_NUM_PSBCH_DMRS_RE, 1, 1);
+#endif
+
+#ifdef SL_DEBUG
+ printf("\nMapping Sidelink PSBCH DMRS, PSBCH modulation symbols to 132 REs\n");
+#endif
+
+#ifdef SL_DEBUG
+ printf("%s. DEBUG PSBCH TX: RE MAPPING of PSBCH DATA AND DMRS \n", __func__);
+ printf("Input Params - StartSYM:%d, NUMSYM:%d, RE_OFFSET:%d, num_REs:%d, scaling_factor:%d, symbol_size:%d\n",
+ 0,
+ numsym,
+ re_offset,
+ numre,
+ scaling_factor,
+ symbol_size);
+#endif
+
+ for (int l = 0; l < numsym;) {
+ int k = re_offset % symbol_size;
+ int symbol_offset = l * symbol_size;
+ int offset = 0;
+
+ for (int m = 0; m < numre; m++) {
+ // Maps PSBCH DMRS in every 4th RE ex:0,4,....128
+ // Maps PSBCH in all other REs ex: 1,2,3,5,6,...127,129,130,131
+
+ offset = symbol_offset + k;
+
+#ifdef SL_DEBUG
+ printf("symbol:%d, symbol_offset:%d, k:%d, re:%d, sampleoffset:%d ", l, symbol_offset, k, m, offset);
+#endif
+
+ if (m % 4 == 0) {
+ txF[offset] = c16xmulConstShift(psbch_dmrs[dmrs_index], scaling_factor, 15);
+
+#ifdef SL_DEBUG
+ printf("txF[%d]:%d,%d, psbch_dmrs[%d]:%d,%d ",
+ offset,
+ txF[offset].r,
+ txF[offset].i,
+ dmrs_index,
+ psbch_dmrs[dmrs_index].r,
+ psbch_dmrs[dmrs_index].i);
+#endif
+
+ dmrs_index++;
+
+ } else {
+ txF[offset] = c16xmulConstShift(psbch_modsym[index], scaling_factor, 15);
+
+#ifdef SL_DEBUG
+ printf("txF[%d]:%d,%d, psbch_modsym[%d]:%d,%d\n",
+ offset,
+ txF[offset].r,
+ txF[offset].i,
+ index,
+ psbch_modsym[index].r,
+ psbch_modsym[index].i);
+#endif
+
+ index++;
+ }
+
+ k = (k + 1) % symbol_size;
+ }
+
+ LOG_D(PHY,
+ "PSBCH TX: 38211 STEP: RE MAPPING OF PSBCH, PSBCH DMRS DONE. symbol:%d, first RE offset:%d, Last RE offset:%d, Num PSBCH "
+ "DATA REs:%d, Num PSBCH DMRS REs:%d\n",
+ l,
+ symbol_offset + re_offset,
+ offset,
+ index,
+ dmrs_index);
+
+ l = (l == 0) ? 5 : l + 1;
+ }
+}
+
+/**
+ *This function prepares the PSBCH block and RE MAPS PSS, SSS, PSBCH DATA, PSBCH DMRS into buffer txF.
+ *Called by the L1 Scheduler when MAC triggers PHY to send PSBCH
+ *UE is the UE context.
+ *frame, slot points to the TTI in which PSBCH TX will be transmitted
+ */
+void nr_tx_psbch(PHY_VARS_NR_UE *UE, uint32_t frame_tx, uint32_t slot_tx, sl_nr_tx_config_psbch_pdu_t *psbch_vars, c16_t **txdataF)
+{
+ sl_nr_ue_phy_params_t *sl_ue_phy_params = &UE->SL_UE_PHY_PARAMS;
+ uint16_t slss_id = psbch_vars->tx_slss_id;
+ NR_DL_FRAME_PARMS *sl_fp = &sl_ue_phy_params->sl_frame_params;
+ uint32_t psbch_payload = *((uint32_t *)psbch_vars->psbch_payload);
+
+ LOG_D(PHY, "PSBCH TX: slss-id %d, psbch payload %x \n", slss_id, psbch_payload);
+
+ // Insert FN and Slot number into SL-MIB
+ uint32_t mask = ~(0x700 | 0xFE0000 | 0x10000 | 0xFC000000);
+ psbch_payload &= mask;
+ psbch_payload |= ((frame_tx % 1024) << 1) & 0x700;
+ psbch_payload |= ((frame_tx % 1024) << 17) & 0xFE0000;
+ psbch_payload |= (slot_tx << 10) & 0x10000;
+ psbch_payload |= (slot_tx << 26) & 0xFC000000;
+
+#ifdef SL_DEBUG
+ printf("DEBUG PSBCH TX: DFN, SLOT included. psbch_a :0x%08x, frame:%d, slot:%d\n", psbch_payload, frame_tx, slot_tx);
+#endif
+
+ LOG_D(PHY, "PSBCH TX: Frame.Slot %d.%d. Payload::0x%08x, slssid:%d\n", frame_tx, slot_tx, psbch_payload, slss_id);
+
+ // GENERATE Sidelink PSS,SSS Sequences, PSBCH DMRS Symbols, PSBCH Symbols
+ int16_t *sl_pss = &sl_ue_phy_params->init_params.sl_pss[slss_id / 336][0];
+ int16_t *sl_sss = &sl_ue_phy_params->init_params.sl_sss[slss_id][0];
+
+ uint16_t re_offset = sl_fp->first_carrier_offset + sl_fp->ssb_start_subcarrier;
+ uint16_t symbol_size = sl_fp->ofdm_symbol_size;
+ // TBD: Need to be replaced by function which calculates scaling factor based on psbch tx power
+ uint16_t scaling_factor = AMP;
+
+ struct complex16 *txF = &txdataF[0][0];
+ uint16_t startsym = SL_NR_PSS_START_SYMBOL;
+
+#ifdef SL_DEBUG
+ printf("DEBUG PSBCH TX: MAP PSS. startsym:%d, PSS RE START:%d, scaling factor:%d\n", startsym, re_offset, scaling_factor);
+#endif
+ sl_map_pss_or_sss(txF, sl_pss, startsym, re_offset, scaling_factor, symbol_size); // PSS
+
+ startsym += SL_NR_NUM_PSS_SYMBOLS;
+#ifdef SL_DEBUG
+ printf("DEBUG PSBCH TX: MAP SSS. startsym:%d, SSS RE START:%d, scaling factor:%d\n", startsym, re_offset, scaling_factor);
+#endif
+ sl_map_pss_or_sss(txF, sl_sss, startsym, re_offset, scaling_factor, symbol_size); // SSS
+
+#ifdef SL_DEBUG
+ printf("DEBUG PSBCH TX: MAP PSBCH DATA AND DMRS. cyclicPrefix:%d, PSS RE START:%d, scaling factor:%d\n",
+ sl_fp->Ncp,
+ re_offset,
+ scaling_factor);
+#endif
+
+ struct complex16 *psbch_dmrs = &sl_ue_phy_params->init_params.psbch_dmrs_modsym[slss_id][0];
+
+ sl_generate_and_map_psbch(txF, &psbch_payload, slss_id, sl_fp->Ncp, re_offset, scaling_factor, symbol_size, psbch_dmrs);
+
+#ifdef SL_DEBUG
+ printf("DEBUG PSBCH TX: txdataF Prepared\n");
+#endif
+
+#ifdef SL_DEBUG
+ LOG_M("sl_psbch_block.m", "sl_txF", (void *)txdataF[0], symbol_size * 14, 1, 1);
+#endif
+}
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h
index 5a4654f360..deafd7a9ee 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h
@@ -418,6 +418,40 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
void dump_nrdlsch(PHY_VARS_NR_UE *ue,uint8_t gNB_id,uint8_t nr_slot_rx,unsigned int *coded_bits_per_codeword,int round, unsigned char harq_pid);
void nr_a_sum_b(c16_t *input_x, c16_t *input_y, unsigned short nb_rb);
+
+int nr_rx_psbch(PHY_VARS_NR_UE *ue,
+ UE_nr_rxtx_proc_t *proc,
+ int estimateSz,
+ struct complex16 dl_ch_estimates[][estimateSz],
+ NR_DL_FRAME_PARMS *frame_parms,
+ uint8_t *decoded_output,
+ c16_t rxdataF[][frame_parms->samples_per_slot_wCP],
+ uint16_t slss_id);
+
+void nr_tx_psbch(PHY_VARS_NR_UE *UE, uint32_t frame_tx, uint32_t slot_tx, sl_nr_tx_config_psbch_pdu_t *psbch_vars, c16_t **txdataF);
+
+nr_initial_sync_t sl_nr_slss_search(PHY_VARS_NR_UE *UE, UE_nr_rxtx_proc_t *proc, int num_frames);
+
+// Reuse already existing PBCH functions
+int nr_pbch_channel_level(struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL],
+ NR_DL_FRAME_PARMS *frame_parms,
+ int nb_re);
+void nr_pbch_channel_compensation(struct complex16 rxdataF_ext[][PBCH_MAX_RE_PER_SYMBOL],
+ struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL],
+ int nb_re,
+ struct complex16 rxdataF_comp[][PBCH_MAX_RE_PER_SYMBOL],
+ NR_DL_FRAME_PARMS *frame_parms,
+ uint8_t output_shift);
+void nr_pbch_unscrambling(int16_t *demod_pbch_e,
+ uint16_t Nid,
+ uint8_t nushift,
+ uint16_t M,
+ uint16_t length,
+ uint8_t bitwise,
+ uint32_t unscrambling_mask,
+ uint32_t pbch_a_prime,
+ uint32_t *pbch_a_interleaved);
+void nr_pbch_quantize(int16_t *pbch_llr8, int16_t *pbch_llr, uint16_t len);
/**@}*/
#endif
diff --git a/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c b/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c
index b2c0a6242b..721697e248 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c
@@ -704,3 +704,93 @@ static int pss_search_time_nr(c16_t **rxdata, PHY_VARS_NR_UE *ue, int fo_flag, i
return peak_position;
}
+void sl_generate_pss(SL_NR_UE_INIT_PARAMS_t *sl_init_params, uint8_t n_sl_id2, uint16_t scaling)
+{
+ int i = 0, m = 0;
+ int16_t x[SL_NR_PSS_SEQUENCE_LENGTH];
+ const int x_initial[7] = {0, 1, 1, 0, 1, 1, 1};
+ int16_t *sl_pss = sl_init_params->sl_pss[n_sl_id2];
+ int16_t *sl_pss_for_sync = sl_init_params->sl_pss_for_sync[n_sl_id2];
+
+ LOG_D(PHY, "SIDELINK PSBCH INIT: PSS Generation with N_SL_id2:%d\n", n_sl_id2);
+
+#ifdef SL_DEBUG_INIT
+ printf("SIDELINK: PSS Generation with N_SL_id2:%d\n", n_sl_id2);
+#endif
+
+ /// Sequence generation
+ for (i = 0; i < 7; i++)
+ x[i] = x_initial[i];
+
+ for (i = 0; i < (SL_NR_PSS_SEQUENCE_LENGTH - 7); i++) {
+ x[i + 7] = (x[i + 4] + x[i]) % 2;
+ }
+
+ for (i = 0; i < SL_NR_PSS_SEQUENCE_LENGTH; i++) {
+ m = (i + 22 + 43 * n_sl_id2) % SL_NR_PSS_SEQUENCE_LENGTH;
+ sl_pss_for_sync[i] = (1 - 2 * x[m]);
+ sl_pss[i] = sl_pss_for_sync[i] * scaling;
+
+#ifdef SL_DEBUG_INIT_DATA
+ printf("m:%d, sl_pss[%d]:%d\n", m, i, sl_pss[i]);
+#endif
+ }
+
+#ifdef SL_DUMP_INIT_SAMPLES
+ LOG_M("sl_pss_seq.m", "sl_pss", (void *)sl_pss, SL_NR_PSS_SEQUENCE_LENGTH, 1, 0);
+#endif
+}
+
+// This cannot be done at init time as ofdm symbol size, ssb start subcarrier depends on configuration
+// done at SLSS read time.
+void sl_generate_pss_ifft_samples(sl_nr_ue_phy_params_t *sl_ue_params, SL_NR_UE_INIT_PARAMS_t *sl_init_params)
+{
+ uint8_t id2 = 0;
+ int16_t *sl_pss = NULL;
+ NR_DL_FRAME_PARMS *sl_fp = &sl_ue_params->sl_frame_params;
+ int16_t scaling_factor = AMP;
+
+ int32_t *pss_T = NULL;
+
+ uint16_t k = 0;
+
+ c16_t pss_F[sl_fp->ofdm_symbol_size]; // IQ samples in freq domain
+
+ LOG_I(PHY, "SIDELINK INIT: Generation of PSS time domain samples. scaling_factor:%d\n", scaling_factor);
+
+ for (id2 = 0; id2 < SL_NR_NUM_IDs_IN_PSS; id2++) {
+ k = sl_fp->first_carrier_offset + sl_fp->ssb_start_subcarrier + 2; // PSS in from REs 2-129
+ if (k >= sl_fp->ofdm_symbol_size)
+ k -= sl_fp->ofdm_symbol_size;
+
+ pss_T = &sl_init_params->sl_pss_for_correlation[id2][0];
+ sl_pss = sl_init_params->sl_pss[id2];
+
+ memset(pss_T, 0, sl_fp->ofdm_symbol_size * sizeof(pss_T[0]));
+ memset(pss_F, 0, sl_fp->ofdm_symbol_size * sizeof(c16_t));
+
+ for (int i = 0; i < SL_NR_PSS_SEQUENCE_LENGTH; i++) {
+ pss_F[k].r = (sl_pss[i] * scaling_factor) >> 15;
+ // pss_F[2*k] = (sl_pss[i]/23170) * 4192;
+ // pss_F[2*k+1] = 0;
+
+#ifdef SL_DEBUG_INIT_DATA
+ printf("id:%d, k:%d, pss_F[%d]:%d, sl_pss[%d]:%d\n", id2, k, 2 * k, pss_F[2 * k], i, sl_pss[i]);
+#endif
+
+ k++;
+ if (k == sl_fp->ofdm_symbol_size)
+ k = 0;
+ }
+
+ idft((int16_t)get_idft(sl_fp->ofdm_symbol_size),
+ (int16_t *)&pss_F[0], /* complex input */
+ (int16_t *)&pss_T[0], /* complex output */
+ 1); /* scaling factor */
+ }
+
+#ifdef SL_DUMP_PSBCH_TX_SAMPLES
+ LOG_M("sl_pss_TD_id0.m", "pss_TD_0", (void *)sl_init_params->sl_pss_for_correlation[0], sl_fp->ofdm_symbol_size, 1, 1);
+ LOG_M("sl_pss_TD_id1.m", "pss_TD_1", (void *)sl_init_params->sl_pss_for_correlation[1], sl_fp->ofdm_symbol_size, 1, 1);
+#endif
+}
diff --git a/openair1/PHY/NR_UE_TRANSPORT/sss_nr.c b/openair1/PHY/NR_UE_TRANSPORT/sss_nr.c
index 08f234483b..09d8ce4f4a 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/sss_nr.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/sss_nr.c
@@ -63,13 +63,6 @@
#define INITIAL_SSS_NR (7)
-static const int16_t phase_re_nr[PHASE_HYPOTHESIS_NUMBER]
- // -pi/3 ---- pi/3
- = {16384, 20173, 23571, 26509, 28932, 30791, 32051, 32687, 32687, 32051, 30791, 28932, 26509, 23571, 20173, 16384};
-
-static const int16_t phase_im_nr[PHASE_HYPOTHESIS_NUMBER] // -pi/3 ---- pi/3
- = {-28377, -25821, -22762, -19260, -15383, -11207, -6813, -2286, 2286, 6813, 11207, 15383, 19260, 22762, 25821, 28377};
-
static int16_t d_sss[N_ID_2_NUMBER][N_ID_1_NUMBER][LENGTH_SSS_NR];
void init_context_sss_nr(int amp)
@@ -475,7 +468,7 @@ bool rx_sss_nr(PHY_VARS_NR_UE *ue,
// This is the inner product using one particular value of each unknown parameter
for (i=0; i < LENGTH_SSS_NR; i++) {
- metric_re += d[i] * ((phase_re_nr[phase] * sss[i].r - phase_im_nr[phase] * sss[i].i) >> SCALING_METRIC_SSS_NR);
+ metric_re += d[i] * ((phase_nr[phase].r * sss[i].r - phase_nr[phase].i * sss[i].i) >> SCALING_METRIC_SSS_NR);
}
metric = metric_re;
@@ -537,3 +530,51 @@ bool rx_sss_nr(PHY_VARS_NR_UE *ue,
return true;
}
+
+void sl_generate_sss(SL_NR_UE_INIT_PARAMS_t *sl_init_params, uint16_t slss_id, uint16_t scaling)
+{
+ int i = 0;
+ int m0, m1;
+ int n_sl_id1, n_sl_id2;
+ int16_t *sl_sss = sl_init_params->sl_sss[slss_id];
+ int16_t *sl_sss_for_sync = sl_init_params->sl_sss_for_sync[slss_id];
+
+ int16_t x0[SL_NR_SSS_SEQUENCE_LENGTH], x1[SL_NR_SSS_SEQUENCE_LENGTH];
+ const int x_initial[7] = {1, 0, 0, 0, 0, 0, 0};
+
+ n_sl_id1 = slss_id % 336;
+ n_sl_id2 = slss_id / 336;
+
+ LOG_D(PHY, "SIDELINK INIT: SSS Generation with N_SL_id1:%d N_SL_id2:%d\n", n_sl_id1, n_sl_id2);
+
+#ifdef SL_DEBUG_INIT
+ printf("SIDELINK: SSS Generation with slss_id:%d, N_SL_id1:%d, N_SL_id2:%d\n", slss_id, n_sl_id1, n_sl_id2);
+#endif
+
+ for (i = 0; i < 7; i++) {
+ x0[i] = x_initial[i];
+ x1[i] = x_initial[i];
+ }
+
+ for (i = 0; i < SL_NR_SSS_SEQUENCE_LENGTH - 7; i++) {
+ x0[i + 7] = (x0[i + 4] + x0[i]) % 2;
+ x1[i + 7] = (x1[i + 1] + x1[i]) % 2;
+ }
+
+ m0 = 15 * (n_sl_id1 / 112) + (5 * n_sl_id2);
+ m1 = n_sl_id1 % 112;
+
+ for (i = 0; i < SL_NR_SSS_SEQUENCE_LENGTH; i++) {
+ sl_sss_for_sync[i] = (1 - 2 * x0[(i + m0) % SL_NR_SSS_SEQUENCE_LENGTH]) * (1 - 2 * x1[(i + m1) % SL_NR_SSS_SEQUENCE_LENGTH]);
+ sl_sss[i] = sl_sss_for_sync[i] * scaling;
+
+#ifdef SL_DEBUG_INIT_DATA
+ printf("m0:%d, m1:%d, sl_sss_for_sync[%d]:%d, sl_sss[%d]:%d\n", m0, m1, i, sl_sss_for_sync[i], i, sl_sss[i]);
+#endif
+ }
+
+#ifdef SL_DUMP_PSBCH_TX_SAMPLES
+ LOG_M("sl_sss_seq.m", "sl_sss", (void *)sl_sss, SL_NR_SSS_SEQUENCE_LENGTH, 1, 0);
+ LOG_M("sl_sss_forsync_seq.m", "sl_sss_for_sync", (void *)sl_sss_for_sync, SL_NR_SSS_SEQUENCE_LENGTH, 1, 0);
+#endif
+}
diff --git a/openair1/PHY/TOOLS/tools_defs.h b/openair1/PHY/TOOLS/tools_defs.h
index 8f93a3fd6b..771f488b54 100644
--- a/openair1/PHY/TOOLS/tools_defs.h
+++ b/openair1/PHY/TOOLS/tools_defs.h
@@ -242,6 +242,11 @@ extern "C" {
};
}
+ __attribute__((always_inline)) inline c16_t c16xmulConstShift(const c16_t a, const int b, const int Shift)
+ {
+ return (c16_t){.r = (int16_t)((a.r * b) >> Shift), .i = (int16_t)((a.i * b) >> Shift)};
+ }
+
__attribute__((always_inline)) inline c32_t c32x16maddShift(const c16_t a, const c16_t b, const c32_t c, const int Shift) {
return (c32_t) {
.r = ((a.r * b.r - a.i * b.i) >> Shift) + c.r,
diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h
index 8bbb1f9d84..c746bd69b8 100644
--- a/openair1/PHY/defs_nr_UE.h
+++ b/openair1/PHY/defs_nr_UE.h
@@ -39,6 +39,7 @@
#include "defs_nr_common.h"
#include "CODING/nrPolar_tools/nr_polar_pbch_defs.h"
+#include "PHY/defs_nr_sl_UE.h"
#include
#include
@@ -591,6 +592,10 @@ typedef struct PHY_VARS_NR_UE_s {
uint8_t *phy_sim_dlsch_b;
notifiedFIFO_t tx_resume_ind_fifo[NR_MAX_SLOTS_PER_FRAME];
+
+ // Sidelink parameters
+ sl_nr_sidelink_mode_t sl_mode;
+ sl_nr_ue_phy_params_t SL_UE_PHY_PARAMS;
} PHY_VARS_NR_UE;
typedef struct {
@@ -612,11 +617,20 @@ typedef struct {
typedef struct nr_phy_data_tx_s {
NR_UE_ULSCH_t ulsch;
NR_UE_PUCCH pucch_vars;
+
+ // Sidelink Rx action decided by MAC
+ sl_nr_tx_config_type_enum_t sl_tx_action;
+ sl_nr_tx_config_psbch_pdu_t psbch_vars;
+
} nr_phy_data_tx_t;
typedef struct nr_phy_data_s {
NR_UE_PDCCH_CONFIG phy_pdcch_config;
NR_UE_DLSCH_t dlsch[2];
+
+ // Sidelink Rx action decided by MAC
+ sl_nr_rx_config_type_enum_t sl_rx_action;
+
} nr_phy_data_t;
/* this structure is used to pass both UE phy vars and
* proc to the function UE_thread_rxn_txnp4
diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h
index 3a7e2032b9..121ef0d3c1 100644
--- a/openair1/PHY/defs_nr_common.h
+++ b/openair1/PHY/defs_nr_common.h
@@ -96,6 +96,8 @@
#define MAX_DELAY_COMP 20
+#define PBCH_MAX_RE_PER_SYMBOL (20 * 12)
+
typedef enum {
NR_MU_0=0,
NR_MU_1,
@@ -168,8 +170,6 @@ struct NR_DL_FRAME_PARMS {
/// Frame type (0 FDD, 1 TDD)
frame_type_t frame_type;
uint8_t tdd_config;
- /// Sidelink Cell ID
- uint16_t Nid_SL;
/// Cell ID
uint16_t Nid_cell;
/// subcarrier spacing (15,30,60,120)
diff --git a/openair1/PHY/defs_nr_sl_UE.h b/openair1/PHY/defs_nr_sl_UE.h
new file mode 100644
index 0000000000..29958ac75a
--- /dev/null
+++ b/openair1/PHY/defs_nr_sl_UE.h
@@ -0,0 +1,152 @@
+/*
+ * 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
+ */
+
+/*! \file PHY/defs_nr_sl_UE.h
+ \brief Top-level defines and structure definitions
+ \author
+ \date
+ \version
+ \company Fraunhofer
+ \email:
+ \note
+ \warning
+*/
+
+#ifndef _DEFS_NR_SL_UE_H_
+#define _DEFS_NR_SL_UE_H_
+
+#include "PHY/types.h"
+#include "PHY/defs_nr_common.h"
+#include "nfapi/open-nFAPI/nfapi/public_inc/sidelink_nr_ue_interface.h"
+#include "common/utils/time_meas.h"
+
+// (33*(13-4))
+// Normal CP - NUM_SSB_Symbols = 13. 4 symbols for PSS, SSS
+#define SL_NR_NUM_PSBCH_DMRS_RE 297
+// ceil(2(QPSK)*SL_NR_NUM_PSBCH_DMRS_RE/32)
+#define SL_NR_NUM_PSBCH_DMRS_RE_DWORD 20
+// 11 RBs for PSBCH in one symbol * 12 REs
+#define SL_NR_NUM_PSBCH_RE_IN_ONE_SYMBOL 132
+// 3 DMRS REs per RB * 11 RBS in one symbol
+#define SL_NR_NUM_PSBCH_DMRS_RE_IN_ONE_SYMBOL 33
+// 9 PSBCH DATA REs * 11 RBS in one symbol
+#define SL_NR_NUM_PSBCH_DATA_RE_IN_ONE_SYMBOL 99
+#define SL_NR_NUM_PSBCH_RBS_IN_ONE_SYMBOL 11
+// SL_NR_POLAR_PSBCH_E_NORMAL_CP/2 bits because QPSK used for PSBCH.
+// 11 * (12-3 DMRS REs) * 9 symbols for PSBCH
+#define SL_NR_NUM_PSBCH_MODULATED_SYMBOLS 891
+#define SL_NR_NUM_PSBCH_DATA_RE_IN_ONE_RB 9
+#define SL_NR_NUM_PSBCH_DMRS_RE_IN_ONE_RB 3
+// 11 * (12-3 DMRS REs) * 9 symbols for PSBCH
+#define SL_NR_NUM_PSBCH_DATA_RE_IN_ALL_SYMBOLS 891
+
+#define SL_NR_NUM_SYMBOLS_SSB_NORMAL_CP 13
+#define SL_NR_NUM_SYMBOLS_SSB_EXT_CP 11
+#define SL_NR_NUM_PSS_SYMBOLS 2
+#define SL_NR_NUM_SSS_SYMBOLS 2
+#define SL_NR_PSS_START_SYMBOL 1
+#define SL_NR_SSS_START_SYMBOL 3
+#define SL_NR_NUM_PSS_OR_SSS_SYMBOLS 2
+#define SL_NR_PSS_SEQUENCE_LENGTH 127
+#define SL_NR_SSS_SEQUENCE_LENGTH 127
+#define SL_NR_NUM_IDs_IN_PSS 2
+#define SL_NR_NUM_IDs_IN_SSS 336
+#define SL_NR_NUM_SLSS_IDs 672
+
+typedef enum sl_nr_sidelink_mode { SL_NOT_SUPPORTED = 0, SL_MODE1_SUPPORTED, SL_MODE2_SUPPORTED } sl_nr_sidelink_mode_t;
+
+//(11*(12-3 DMRS REs) * 2 (QPSK used)
+#define SL_NR_NUM_PSBCH_DATA_BITS_IN_ONE_SYMBOL 198
+
+typedef struct SL_NR_UE_INIT_PARAMS {
+ // gold sequences for PSBCH DMRS
+ uint32_t psbch_dmrs_gold_sequences[SL_NR_NUM_SLSS_IDs][SL_NR_NUM_PSBCH_DMRS_RE_DWORD]; // Gold sequences for PSBCH DMRS
+
+ // PSBCH DMRS QPSK modulated symbols for all possible SLSS Ids
+ struct complex16 psbch_dmrs_modsym[SL_NR_NUM_SLSS_IDs][SL_NR_NUM_PSBCH_DMRS_RE];
+
+ // Scaled values
+ int16_t sl_pss[SL_NR_NUM_IDs_IN_PSS][SL_NR_PSS_SEQUENCE_LENGTH];
+ int16_t sl_sss[SL_NR_NUM_SLSS_IDs][SL_NR_SSS_SEQUENCE_LENGTH];
+
+ // Contains Not scaled values just the simple generated sequence
+ int16_t sl_pss_for_sync[SL_NR_NUM_IDs_IN_PSS][SL_NR_PSS_SEQUENCE_LENGTH];
+ int16_t sl_sss_for_sync[SL_NR_NUM_SLSS_IDs][SL_NR_SSS_SEQUENCE_LENGTH];
+
+ int32_t **sl_pss_for_correlation; // IFFT samples for correlation
+
+} SL_NR_UE_INIT_PARAMS_t;
+
+typedef struct SL_NR_SYNC_PARAMS {
+ // Indicating start of SSB block in the initial set of samples
+ uint32_t ssb_offset;
+ // Freq Offset calculated
+ int32_t freq_offset;
+
+ uint32_t remaining_frames;
+ uint32_t rx_offset;
+ uint32_t slot_offset;
+ uint16_t N_sl_id2; // id2 determined from PSS during sync ref UE selection
+ uint16_t N_sl_id1; // id2 determined from SSS during sync ref UE selection
+ uint16_t N_sl_id; // ID calculated from ID1 and ID2
+ int32_t psbch_rsrp; // rsrp of the decoded psbch during sync ref ue selection
+ uint32_t DFN; // DFN calculated after sync ref UE search
+
+} SL_NR_SYNC_PARAMS_t;
+
+typedef struct SL_NR_UE_PSBCH {
+ // AVG POWER OF PSBCH DMRS in dB/RE
+ int16_t rsrp_dB_per_RE;
+ // AVG POWER OF PSBCH DMRS in dBm/RE
+ int16_t rsrp_dBm_per_RE;
+
+ // STATS - CRC Errors observed during PSBCH reception
+ uint16_t rx_errors;
+
+ // STATS - Receptions with CRC OK
+ uint16_t rx_ok;
+
+ // STATS - transmissions of PSBCH by the UE
+ uint16_t num_psbch_tx;
+
+} SL_NR_UE_PSBCH_t;
+
+typedef struct sl_nr_ue_phy_params {
+ SL_NR_UE_INIT_PARAMS_t init_params;
+
+ SL_NR_SYNC_PARAMS_t sync_params;
+
+ // Sidelink PHY PARAMETERS USED FOR PSBCH reception/Txn
+ SL_NR_UE_PSBCH_t psbch;
+
+ // Configuration parameters from MAC
+ sl_nr_phy_config_request_t sl_config;
+
+ NR_DL_FRAME_PARMS sl_frame_params;
+
+ time_stats_t phy_proc_sl_tx;
+ time_stats_t phy_proc_sl_rx;
+ time_stats_t channel_estimation_stats;
+ time_stats_t ue_sl_indication_stats;
+
+} sl_nr_ue_phy_params_t;
+
+#endif
\ No newline at end of file
diff --git a/openair1/SCHED_NR_UE/defs.h b/openair1/SCHED_NR_UE/defs.h
index f6b33359c8..b34d021390 100644
--- a/openair1/SCHED_NR_UE/defs.h
+++ b/openair1/SCHED_NR_UE/defs.h
@@ -165,5 +165,23 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
const UE_nr_rxtx_proc_t *proc,
c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]);
+int psbch_pscch_processing(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data);
+int phy_procedures_nrUE_SL_TX(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, nr_phy_data_tx_t *phy_data);
+/*! \brief This function prepares the sl indication to pass to the MAC
+ */
+void nr_fill_sl_indication(nr_sidelink_indication_t *sl_ind,
+ sl_nr_rx_indication_t *rx_ind,
+ sl_nr_sci_indication_t *sci_ind,
+ UE_nr_rxtx_proc_t *proc,
+ PHY_VARS_NR_UE *ue,
+ void *phy_data);
+void nr_fill_sl_rx_indication(sl_nr_rx_indication_t *rx_ind,
+ uint8_t pdu_type,
+ PHY_VARS_NR_UE *ue,
+ uint16_t n_pdus,
+ UE_nr_rxtx_proc_t *proc,
+ void *typeSpecific,
+ uint16_t rx_slss_id);
+
#endif
/** @}*/
diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
index ee250f528e..da734cc84d 100644
--- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
+++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
@@ -865,22 +865,22 @@ int pbch_pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_
__attribute__ ((aligned(32))) struct complex16 dl_ch_estimates_time[fp->nb_antennas_rx][fp->ofdm_symbol_size];
for (int i=1; i<4; i++) {
- nr_slot_fep(ue,
- proc,
- (ssb_start_symbol+i)%(fp->symbols_per_slot),
- rxdataF);
+ nr_slot_fep(ue, fp, proc, (ssb_start_symbol + i) % (fp->symbols_per_slot), rxdataF, link_type_dl);
start_meas(&ue->dlsch_channel_estimation_stats);
nr_pbch_channel_estimation(ue,
+ &ue->frame_parms,
estimateSz,
dl_ch_estimates,
dl_ch_estimates_time,
proc,
- (ssb_start_symbol+i)%(fp->symbols_per_slot),
- i-1,
- ssb_index&7,
+ (ssb_start_symbol + i) % (fp->symbols_per_slot),
+ i - 1,
+ ssb_index & 7,
ssb_slot_2 == nr_slot_rx,
- rxdataF);
+ rxdataF,
+ false,
+ fp->Nid_cell);
stop_meas(&ue->dlsch_channel_estimation_stats);
}
@@ -921,10 +921,7 @@ int pbch_pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_
{
for(int j = prs_config->SymbolStart; j < (prs_config->SymbolStart+prs_config->NumPRSSymbols); j++)
{
- nr_slot_fep(ue,
- proc,
- (j%fp->symbols_per_slot),
- rxdataF);
+ nr_slot_fep(ue, fp, proc, (j % fp->symbols_per_slot), rxdataF, link_type_dl);
}
nr_prs_channel_estimation(gNB_id, rsc_id, i, ue, proc, fp, rxdataF);
}
@@ -955,10 +952,7 @@ int pbch_pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_
for (uint16_t l=0; lofdm_demod_stats);
- nr_slot_fep(ue,
- proc,
- l,
- rxdataF);
+ nr_slot_fep(ue, fp, proc, l, rxdataF, link_type_dl);
}
// Hold the channel estimates in frequency domain.
@@ -1016,9 +1010,11 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_
for (uint16_t m=start_symb_sch;m<(nb_symb_sch+start_symb_sch) ; m++){
nr_slot_fep(ue,
+ &ue->frame_parms,
proc,
- m, //to be updated from higher layer
- rxdataF);
+ m, // to be updated from higher layer
+ rxdataF,
+ link_type_dl);
}
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PDSCH, VCD_FUNCTION_OUT);
@@ -1106,7 +1102,7 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_
}
l_csiim[symb_idx] = ue->csiim_vars[gNB_id]->csiim_config_pdu.l_csiim[symb_idx];
if(nr_slot_fep_done == false) {
- nr_slot_fep(ue, proc, ue->csiim_vars[gNB_id]->csiim_config_pdu.l_csiim[symb_idx], rxdataF);
+ nr_slot_fep(ue, &ue->frame_parms, proc, ue->csiim_vars[gNB_id]->csiim_config_pdu.l_csiim[symb_idx], rxdataF, link_type_dl);
}
}
nr_ue_csi_im_procedures(ue, proc, rxdataF);
@@ -1117,7 +1113,7 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_
if ((ue->csirs_vars[gNB_id]) && (ue->csirs_vars[gNB_id]->active == 1)) {
for(int symb = 0; symb < NR_SYMBOLS_PER_SLOT; symb++) {
if(is_csi_rs_in_symbol(ue->csirs_vars[gNB_id]->csirs_config_pdu,symb)) {
- nr_slot_fep(ue, proc, symb, rxdataF);
+ nr_slot_fep(ue, &ue->frame_parms, proc, symb, rxdataF, link_type_dl);
}
}
nr_ue_csi_rs_procedures(ue, proc, rxdataF);
diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c
new file mode 100644
index 0000000000..2bf8ee6cb9
--- /dev/null
+++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c
@@ -0,0 +1,305 @@
+/*
+ * 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
+ */
+
+#define _GNU_SOURCE
+
+#include "PHY/defs_nr_UE.h"
+#include
+#include "common/utils/LOG/log.h"
+#include "common/utils/LOG/vcd_signal_dumper.h"
+#include "UTIL/OPT/opt.h"
+#include "intertask_interface.h"
+#include "T.h"
+#include "PHY/MODULATION/modulation_UE.h"
+#include "PHY/NR_UE_ESTIMATION/nr_estimation.h"
+#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
+
+void nr_fill_sl_indication(nr_sidelink_indication_t *sl_ind,
+ sl_nr_rx_indication_t *rx_ind,
+ sl_nr_sci_indication_t *sci_ind,
+ UE_nr_rxtx_proc_t *proc,
+ PHY_VARS_NR_UE *ue,
+ void *phy_data)
+{
+ memset((void *)sl_ind, 0, sizeof(nr_sidelink_indication_t));
+
+ sl_ind->gNB_index = proc->gNB_id;
+ sl_ind->module_id = ue->Mod_id;
+ sl_ind->cc_id = ue->CC_id;
+ sl_ind->frame_rx = proc->frame_rx;
+ sl_ind->slot_rx = proc->nr_slot_rx;
+ sl_ind->frame_tx = proc->frame_tx;
+ sl_ind->slot_tx = proc->nr_slot_tx;
+ sl_ind->phy_data = phy_data;
+
+ if (rx_ind) {
+ sl_ind->rx_ind = rx_ind; // hang on rx_ind instance
+ sl_ind->sci_ind = NULL;
+ }
+ if (sci_ind) {
+ sl_ind->rx_ind = NULL;
+ sl_ind->sci_ind = sci_ind;
+ }
+}
+
+void nr_fill_sl_rx_indication(sl_nr_rx_indication_t *rx_ind,
+ uint8_t pdu_type,
+ PHY_VARS_NR_UE *ue,
+ uint16_t n_pdus,
+ UE_nr_rxtx_proc_t *proc,
+ void *typeSpecific,
+ uint16_t rx_slss_id)
+{
+ if (n_pdus > 1) {
+ LOG_E(PHY, "In %s: multiple number of SL PDUs not supported yet...\n", __FUNCTION__);
+ }
+
+ sl_nr_ue_phy_params_t *sl_phy_params = &ue->SL_UE_PHY_PARAMS;
+
+ switch (pdu_type) {
+ case SL_NR_RX_PDU_TYPE_SLSCH:
+ break;
+ case FAPI_NR_RX_PDU_TYPE_SSB: {
+ sl_nr_ssb_pdu_t *ssb_pdu = &rx_ind->rx_indication_body[n_pdus - 1].ssb_pdu;
+ if (typeSpecific) {
+ uint8_t *psbch_decoded_output = (uint8_t *)typeSpecific;
+ memcpy(ssb_pdu->psbch_payload, psbch_decoded_output, sizeof(4)); // 4 bytes of PSBCH payload bytes
+ ssb_pdu->rsrp_dbm = sl_phy_params->psbch.rsrp_dBm_per_RE;
+ ssb_pdu->rx_slss_id = rx_slss_id;
+ ssb_pdu->decode_status = true;
+ LOG_D(PHY,
+ "SL-IND: SSB to MAC. rsrp:%d, slssid:%d, payload:%x\n",
+ ssb_pdu->rsrp_dbm,
+ ssb_pdu->rx_slss_id,
+ *((uint32_t *)(ssb_pdu->psbch_payload)));
+ } else
+ ssb_pdu->decode_status = false;
+ } break;
+ default:
+ break;
+ }
+
+ rx_ind->rx_indication_body[n_pdus - 1].pdu_type = pdu_type;
+ rx_ind->number_pdus = n_pdus;
+}
+
+static int nr_ue_psbch_procedures(PHY_VARS_NR_UE *ue,
+ NR_DL_FRAME_PARMS *fp,
+ UE_nr_rxtx_proc_t *proc,
+ int estimateSz,
+ struct complex16 dl_ch_estimates[][estimateSz],
+ nr_phy_data_t *phy_data,
+ c16_t rxdataF[][fp->samples_per_slot_wCP])
+{
+ int ret = 0;
+ DevAssert(ue);
+
+ int frame_rx = proc->frame_rx;
+ int nr_slot_rx = proc->nr_slot_rx;
+
+ sl_nr_ue_phy_params_t *sl_phy_params = &ue->SL_UE_PHY_PARAMS;
+ uint16_t rx_slss_id = sl_phy_params->sl_config.sl_sync_source.rx_slss_id;
+
+ // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_PSBCH_PROCEDURES, VCD_FUNCTION_IN);
+
+ LOG_D(PHY,
+ "[UE %d] Frame %d Slot %d, Trying PSBCH (SLSS ID %d)\n",
+ ue->Mod_id,
+ frame_rx,
+ nr_slot_rx,
+ sl_phy_params->sl_config.sl_sync_source.rx_slss_id);
+
+ uint8_t decoded_pdu[4] = {0};
+ ret = nr_rx_psbch(ue,
+ proc,
+ estimateSz,
+ dl_ch_estimates,
+ fp,
+ decoded_pdu,
+ rxdataF,
+ sl_phy_params->sl_config.sl_sync_source.rx_slss_id);
+
+ nr_sidelink_indication_t sl_indication;
+ sl_nr_rx_indication_t rx_ind = {0};
+ uint16_t number_pdus = 1;
+
+ uint8_t *result = NULL;
+ if (ret) {
+ sl_phy_params->psbch.rx_errors++;
+ LOG_E(PHY, "%d:%d PSBCH RX: NOK \n", proc->frame_rx, proc->nr_slot_rx);
+ } else {
+ result = decoded_pdu;
+ sl_phy_params->psbch.rx_ok++;
+ LOG_D(PHY, "%d:%d PSBCH RX: OK \n", proc->frame_rx, proc->nr_slot_rx);
+ }
+
+ nr_fill_sl_indication(&sl_indication, &rx_ind, NULL, proc, ue, phy_data);
+ nr_fill_sl_rx_indication(&rx_ind, SL_NR_RX_PDU_TYPE_SSB, ue, number_pdus, proc, (void *)result, rx_slss_id);
+
+ if (ue->if_inst && ue->if_inst->sl_indication)
+ ue->if_inst->sl_indication(&sl_indication);
+
+ // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_PSBCH_PROCEDURES, VCD_FUNCTION_OUT);
+ return ret;
+}
+
+int psbch_pscch_processing(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data)
+{
+ int frame_rx = proc->frame_rx;
+ int nr_slot_rx = proc->nr_slot_rx;
+ sl_nr_ue_phy_params_t *sl_phy_params = &ue->SL_UE_PHY_PARAMS;
+ NR_DL_FRAME_PARMS *fp = &sl_phy_params->sl_frame_params;
+ int sampleShift = 0;
+
+ // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_RX_SL, VCD_FUNCTION_IN);
+ start_meas(&sl_phy_params->phy_proc_sl_rx);
+
+ LOG_D(PHY, " ****** Sidelink RX-Chain for Frame.Slot %d.%d ****** \n", frame_rx % 1024, nr_slot_rx);
+
+ const uint32_t rxdataF_sz = fp->samples_per_slot_wCP;
+ __attribute__((aligned(32))) c16_t rxdataF[fp->nb_antennas_rx][rxdataF_sz];
+
+ if (phy_data->sl_rx_action == SL_NR_CONFIG_TYPE_RX_PSBCH) {
+ const int estimateSz = fp->symbols_per_slot * fp->ofdm_symbol_size;
+
+ // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PSBCH, VCD_FUNCTION_IN);
+ LOG_D(PHY, " ----- PSBCH RX TTI: frame.slot %d.%d ------ \n", frame_rx % 1024, nr_slot_rx);
+
+ __attribute__((aligned(32))) struct complex16 dl_ch_estimates[fp->nb_antennas_rx][estimateSz];
+ __attribute__((aligned(32))) struct complex16 dl_ch_estimates_time[fp->nb_antennas_rx][fp->ofdm_symbol_size];
+
+ // 0 for Normal Cyclic Prefix and 1 for EXT CyclicPrefix
+ const int numsym = (fp->Ncp) ? SL_NR_NUM_SYMBOLS_SSB_EXT_CP : SL_NR_NUM_SYMBOLS_SSB_NORMAL_CP;
+
+ for (int sym = 0; sym < numsym;) {
+ nr_slot_fep(ue, fp, proc, sym, rxdataF, link_type_ul);
+
+ start_meas(&sl_phy_params->channel_estimation_stats);
+ nr_pbch_channel_estimation(ue,
+ fp,
+ estimateSz,
+ dl_ch_estimates,
+ dl_ch_estimates_time,
+ proc,
+ sym,
+ sym,
+ 0,
+ 0,
+ rxdataF,
+ true,
+ sl_phy_params->sl_config.sl_sync_source.rx_slss_id);
+ stop_meas(&sl_phy_params->channel_estimation_stats);
+
+ // PSBCH present in symbols 0, 5-12 for normal cp
+ sym = (sym == 0) ? 5 : sym + 1;
+ }
+
+ nr_sl_psbch_rsrp_measurements(sl_phy_params, fp, rxdataF, false);
+
+ LOG_D(PHY, " ------ Decode SL-MIB: frame.slot %d.%d ------ \n", frame_rx % 1024, nr_slot_rx);
+
+ const int psbchSuccess = nr_ue_psbch_procedures(ue, fp, proc, estimateSz, dl_ch_estimates, phy_data, rxdataF);
+
+ if (ue->no_timing_correction == 0 && psbchSuccess == 0) {
+ LOG_D(PHY, "start adjust sync slot = %d no timing %d\n", nr_slot_rx, ue->no_timing_correction);
+ sampleShift =
+ nr_adjust_synch_ue(fp, ue, proc->gNB_id, fp->ofdm_symbol_size, dl_ch_estimates_time, frame_rx, nr_slot_rx, 16384);
+ }
+
+ LOG_D(PHY, "Doing N0 measurements in %s\n", __FUNCTION__);
+ // nr_ue_rrc_measurements(ue, proc, rxdataF);
+ // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PSBCH, VCD_FUNCTION_OUT);
+
+ if (frame_rx % 64 == 0) {
+ LOG_I(NR_PHY, "============================================\n");
+
+ LOG_I(NR_PHY,
+ "[UE%d] %d:%d PSBCH Stats: TX %d, RX ok %d, RX not ok %d\n",
+ ue->Mod_id,
+ frame_rx,
+ nr_slot_rx,
+ sl_phy_params->psbch.num_psbch_tx,
+ sl_phy_params->psbch.rx_ok,
+ sl_phy_params->psbch.rx_errors);
+
+ LOG_I(NR_PHY, "============================================\n");
+ }
+ }
+
+ return sampleShift;
+}
+
+int phy_procedures_nrUE_SL_TX(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, nr_phy_data_tx_t *phy_data)
+{
+ int slot_tx = proc->nr_slot_tx;
+ int frame_tx = proc->frame_tx;
+ int tx_action = 0;
+
+ sl_nr_ue_phy_params_t *sl_phy_params = &ue->SL_UE_PHY_PARAMS;
+ NR_DL_FRAME_PARMS *fp = &sl_phy_params->sl_frame_params;
+
+ // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX_SL,VCD_FUNCTION_IN);
+
+ const int samplesF_per_slot = NR_SYMBOLS_PER_SLOT * fp->ofdm_symbol_size;
+ c16_t txdataF_buf[fp->nb_antennas_tx * samplesF_per_slot] __attribute__((aligned(32)));
+ memset(txdataF_buf, 0, sizeof(txdataF_buf));
+ c16_t *txdataF[fp->nb_antennas_tx]; /* workaround to be compatible with current txdataF usage in all tx procedures. */
+ for (int i = 0; i < fp->nb_antennas_tx; ++i)
+ txdataF[i] = &txdataF_buf[i * samplesF_per_slot];
+
+ LOG_D(PHY, "****** start Sidelink TX-Chain for AbsSubframe %d.%d ******\n", frame_tx, slot_tx);
+
+ start_meas(&sl_phy_params->phy_proc_sl_tx);
+
+ if (phy_data->sl_tx_action == SL_NR_CONFIG_TYPE_TX_PSBCH) {
+ sl_nr_tx_config_psbch_pdu_t *psbch_vars = &phy_data->psbch_vars;
+ nr_tx_psbch(ue, frame_tx, slot_tx, psbch_vars, txdataF);
+ sl_phy_params->psbch.num_psbch_tx++;
+
+ if (frame_tx % 64 == 0) {
+ LOG_I(NR_PHY, "============================================\n");
+
+ LOG_I(NR_PHY,
+ "[UE%d] %d:%d PSBCH Stats: TX %d, RX ok %d, RX not ok %d\n",
+ ue->Mod_id,
+ frame_tx,
+ slot_tx,
+ sl_phy_params->psbch.num_psbch_tx,
+ sl_phy_params->psbch.rx_ok,
+ sl_phy_params->psbch.rx_errors);
+
+ LOG_I(NR_PHY, "============================================\n");
+ }
+ tx_action = 1;
+ }
+
+ if (tx_action) {
+ LOG_D(PHY, "Sending Uplink data \n");
+ nr_ue_pusch_common_procedures(ue, proc->nr_slot_tx, fp, fp->nb_antennas_tx, txdataF);
+ }
+
+ LOG_D(PHY, "****** end Sidelink TX-Chain for AbsSubframe %d.%d ******\n", frame_tx, slot_tx);
+
+ // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX_SL, VCD_FUNCTION_OUT);
+ stop_meas(&sl_phy_params->phy_proc_sl_tx);
+
+ return tx_action;
+}
diff --git a/openair1/SIMULATION/NR_PHY/pbchsim.c b/openair1/SIMULATION/NR_PHY/pbchsim.c
index 489931adcc..acfa696b24 100644
--- a/openair1/SIMULATION/NR_PHY/pbchsim.c
+++ b/openair1/SIMULATION/NR_PHY/pbchsim.c
@@ -786,36 +786,45 @@ int main(int argc, char **argv)
int ssb_slot = (UE->symbol_offset/14)+(n_hf*(frame_parms->slots_per_frame>>1));
proc.nr_slot_rx = ssb_slot;
proc.gNB_id = 0;
- for (int i=UE->symbol_offset+1; isymbol_offset+4; i++) {
- nr_slot_fep(UE,
- &proc,
- i%frame_parms->symbols_per_slot,
- rxdataF);
-
- nr_pbch_channel_estimation(UE,estimateSz, dl_ch_estimates, dl_ch_estimates_time, &proc,
- i%frame_parms->symbols_per_slot,i-(UE->symbol_offset+1),ssb_index%8,n_hf,rxdataF);
+ for (int i = UE->symbol_offset + 1; i < UE->symbol_offset + 4; i++) {
+ nr_slot_fep(UE, frame_parms, &proc, i % frame_parms->symbols_per_slot, rxdataF, link_type_dl);
+ nr_pbch_channel_estimation(UE,
+ &UE->frame_parms,
+ estimateSz,
+ dl_ch_estimates,
+ dl_ch_estimates_time,
+ &proc,
+ i % frame_parms->symbols_per_slot,
+ i - (UE->symbol_offset + 1),
+ ssb_index % 8,
+ n_hf,
+ rxdataF,
+ false,
+ frame_parms->Nid_cell);
}
- fapiPbch_t result;
+ fapiPbch_t result;
ret = nr_rx_pbch(UE, &proc, estimateSz, dl_ch_estimates, frame_parms, ssb_index % 8, &result, rxdataF);
- if (ret==0) {
- //UE->rx_ind.rx_indication_body->mib_pdu.ssb_index; //not yet detected automatically
- //UE->rx_ind.rx_indication_body->mib_pdu.ssb_length; //Lmax, not yet detected automatically
- uint8_t gNB_xtra_byte=0;
- for (int i=0; i<8; i++)
- gNB_xtra_byte |= ((gNB->pbch.pbch_a>>(31-i))&1)<<(7-i);
-
- payload_ret = (result.xtra_byte == gNB_xtra_byte);
- for (i=0;i<3;i++){
- payload_ret += (result.decoded_output[i] == ((msgDataTx.ssb[ssb_index].ssb_pdu.ssb_pdu_rel15.bchPayload>>(8*i)) & 0xff));
- }
- //printf("ret %d\n", payload_ret);
- if (payload_ret!=4)
- n_errors_payload++;
- }
+ if (ret == 0) {
+ // UE->rx_ind.rx_indication_body->mib_pdu.ssb_index; //not yet detected automatically
+ // UE->rx_ind.rx_indication_body->mib_pdu.ssb_length; //Lmax, not yet detected automatically
+ uint8_t gNB_xtra_byte = 0;
+ for (int i = 0; i < 8; i++)
+ gNB_xtra_byte |= ((gNB->pbch.pbch_a >> (31 - i)) & 1) << (7 - i);
- if (ret!=0) n_errors++;
+ payload_ret = (result.xtra_byte == gNB_xtra_byte);
+ for (i = 0; i < 3; i++) {
+ payload_ret +=
+ (result.decoded_output[i] == ((msgDataTx.ssb[ssb_index].ssb_pdu.ssb_pdu_rel15.bchPayload >> (8 * i)) & 0xff));
+ }
+ // printf("ret %d\n", payload_ret);
+ if (payload_ret != 4)
+ n_errors_payload++;
+ }
+
+ if (ret != 0)
+ n_errors++;
}
} //noise trials
printf("SNR %f: trials %d, n_errors_crc = %d, n_errors_payload %d\n", SNR,n_trials,n_errors,n_errors_payload);
diff --git a/openair1/SIMULATION/NR_PHY/psbchsim.c b/openair1/SIMULATION/NR_PHY/psbchsim.c
new file mode 100644
index 0000000000..b30405bef9
--- /dev/null
+++ b/openair1/SIMULATION/NR_PHY/psbchsim.c
@@ -0,0 +1,655 @@
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "common/config/config_userapi.h"
+#include "common/ran_context.h"
+#include "PHY/types.h"
+#include "PHY/defs_nr_common.h"
+#include "PHY/defs_nr_UE.h"
+#include "PHY/defs_gNB.h"
+#include "PHY/phy_vars.h"
+#include "NR_MasterInformationBlockSidelink.h"
+#include "PHY/INIT/phy_init.h"
+#include "openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h"
+#include "openair1/SIMULATION/TOOLS/sim.h"
+#include "common/utils/nr/nr_common.h"
+#include "openair2/RRC/NR/nr_rrc_extern.h"
+#include "openair2/RRC/LTE/rrc_vars.h"
+#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
+#include "PHY/INIT/nr_phy_init.h"
+#include "SIMULATION/RF/rf.h"
+#include "common/utils/load_module_shlib.h"
+#include "PHY/MODULATION/nr_modulation.h"
+#include "NR_SL-SSB-TimeAllocation-r16.h"
+
+void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req)
+{
+ abort();
+}
+void e1_bearer_context_modif(const e1ap_bearer_setup_req_t *req)
+{
+ abort();
+}
+void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd)
+{
+ abort();
+}
+void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
+{
+ const char *msg = s == NULL ? "no comment" : s;
+ printf("Exiting at: %s:%d %s(), %s\n", file, line, function, msg);
+ exit(-1);
+}
+int8_t nr_rrc_RA_succeeded(const module_id_t mod_id, const uint8_t gNB_index)
+{
+ return 1;
+}
+// to solve link errors
+double cpuf;
+// void init_downlink_harq_status(NR_DL_UE_HARQ_t *dl_harq) {}
+void get_num_re_dmrs(nfapi_nr_ue_pusch_pdu_t *pusch_pdu, uint8_t *nb_dmrs_re_per_rb, uint16_t *number_dmrs_symbols)
+{
+}
+uint64_t downlink_frequency[1][1];
+int32_t uplink_frequency_offset[1][1];
+THREAD_STRUCT thread_struct;
+instance_t DUuniqInstance = 0;
+instance_t CUuniqInstance = 0;
+openair0_config_t openair0_cfg[1];
+
+RAN_CONTEXT_t RC;
+int oai_exit = 0;
+char *uecap_file;
+
+void nr_rrc_ue_generate_RRCSetupRequest(module_id_t module_id, const uint8_t gNB_index)
+{
+ return;
+}
+
+int8_t nr_mac_rrc_data_req_ue(const module_id_t Mod_idP,
+ const int CC_id,
+ const uint8_t gNB_id,
+ const frame_t frameP,
+ const rb_id_t Srb_id,
+ uint8_t *buffer_pP)
+{
+ return 0;
+}
+
+nr_bler_struct nr_bler_data[NR_NUM_MCS];
+void get_nrUE_params(void)
+{
+ return;
+}
+uint8_t check_if_ue_is_sl_syncsource()
+{
+ return 0;
+}
+//////////////////////////////////////////////////////////////////////////
+static void prepare_mib_bits(uint8_t *buf, uint32_t frame_tx, uint32_t slot_tx)
+{
+ NR_MasterInformationBlockSidelink_t *sl_mib;
+ asn_enc_rval_t enc_rval;
+
+ void *buffer = (void *)buf;
+
+ sl_mib = CALLOC(1, sizeof(NR_MasterInformationBlockSidelink_t));
+
+ sl_mib->inCoverage_r16 = 0; // TRUE;
+
+ // allocate buffer for 7 bits slotnumber
+ sl_mib->slotIndex_r16.size = 1;
+ sl_mib->slotIndex_r16.buf = CALLOC(1, sl_mib->slotIndex_r16.size);
+ sl_mib->slotIndex_r16.bits_unused = sl_mib->slotIndex_r16.size * 8 - 7;
+ sl_mib->slotIndex_r16.buf[0] = slot_tx << sl_mib->slotIndex_r16.bits_unused;
+
+ sl_mib->directFrameNumber_r16.size = 2;
+ sl_mib->directFrameNumber_r16.buf = CALLOC(1, sl_mib->directFrameNumber_r16.size);
+ sl_mib->directFrameNumber_r16.bits_unused = sl_mib->directFrameNumber_r16.size * 8 - 10;
+ sl_mib->directFrameNumber_r16.buf[0] = frame_tx >> (8 - sl_mib->directFrameNumber_r16.bits_unused);
+ sl_mib->directFrameNumber_r16.buf[1] = frame_tx << sl_mib->directFrameNumber_r16.bits_unused;
+
+ enc_rval = uper_encode_to_buffer(&asn_DEF_NR_MasterInformationBlockSidelink, NULL, (void *)sl_mib, buffer, 100);
+
+ AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
+
+ asn_DEF_NR_MasterInformationBlockSidelink.op->free_struct(&asn_DEF_NR_MasterInformationBlockSidelink,
+ sl_mib,
+ ASFM_FREE_EVERYTHING);
+}
+
+static int test_rx_mib(uint8_t *decoded_output, uint16_t frame, uint16_t slot)
+{
+ uint32_t sl_mib = *(uint32_t *)decoded_output;
+
+ uint32_t fn = 0, sl = 0;
+ fn = (((sl_mib & 0x0700) >> 1) | ((sl_mib & 0xFE0000) >> 17));
+ sl = (((sl_mib & 0x010000) >> 10) | ((sl_mib & 0xFC000000) >> 26));
+
+ printf("decoded output:%x, TX %d:%d, timing decoded from sl-MIB %d:%d\n", *(uint32_t *)decoded_output, frame, slot, fn, sl);
+
+ if (frame == fn && slot == sl)
+ return 0;
+
+ return -1;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+static void configure_NR_UE(PHY_VARS_NR_UE *UE, int mu, int N_RB)
+{
+ fapi_nr_config_request_t config;
+ NR_DL_FRAME_PARMS *fp = &UE->frame_parms;
+
+ config.ssb_config.scs_common = mu;
+ config.cell_config.frame_duplex_type = TDD;
+ config.carrier_config.dl_grid_size[mu] = N_RB;
+ config.carrier_config.ul_grid_size[mu] = N_RB;
+ config.carrier_config.dl_frequency = 0;
+ config.carrier_config.uplink_frequency = 0;
+
+ int band;
+ if (mu == 1)
+ band = 78;
+ if (mu == 0)
+ band = 34;
+ nr_init_frame_parms_ue(fp, &config, band);
+ fp->ofdm_offset_divisor = 8;
+ nr_dump_frame_parms(fp);
+
+ if (init_nr_ue_signal(UE, 1) != 0) {
+ printf("Error at UE NR initialisation\n");
+ exit(-1);
+ }
+}
+
+static void sl_init_frame_parameters(PHY_VARS_NR_UE *UE)
+{
+ NR_DL_FRAME_PARMS *nr_fp = &UE->frame_parms;
+ NR_DL_FRAME_PARMS *sl_fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params;
+
+ memcpy(sl_fp, nr_fp, sizeof(NR_DL_FRAME_PARMS));
+ sl_fp->ofdm_offset_divisor = 8; // What is this used for?
+
+ sl_fp->att_tx = 1;
+ sl_fp->att_rx = 1;
+ // band47 //UL freq will be set to Sidelink freq
+ sl_fp->ul_CarrierFreq = 5880000000;
+
+ sl_fp->ssb_start_subcarrier = UE->SL_UE_PHY_PARAMS.sl_config.sl_bwp_config.sl_ssb_offset_point_a;
+ sl_fp->Nid_cell = UE->SL_UE_PHY_PARAMS.sl_config.sl_sync_source.rx_slss_id;
+
+#ifdef DEBUG_INIT
+ LOG_I(PHY, "Dumping Sidelink Frame Parameters\n");
+ nr_dump_frame_parms(sl_fp);
+#endif
+}
+
+static void configure_SL_UE(PHY_VARS_NR_UE *UE, int mu, int N_RB, int ssb_offset, int slss_id)
+{
+ sl_nr_phy_config_request_t *config = &UE->SL_UE_PHY_PARAMS.sl_config;
+ NR_DL_FRAME_PARMS *fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params;
+
+ config->sl_bwp_config.sl_scs = mu;
+ config->sl_bwp_config.sl_ssb_offset_point_a = ssb_offset;
+ config->sl_carrier_config.sl_bandwidth = N_RB;
+ config->sl_carrier_config.sl_grid_size = 106;
+ config->sl_sync_source.rx_slss_id = slss_id;
+
+ sl_init_frame_parameters(UE);
+ sl_ue_phy_init(UE);
+ init_symbol_rotation(fp);
+ init_timeshift_rotation(fp);
+ LOG_I(PHY, "Dumping Sidelink Frame Parameters\n");
+ nr_dump_frame_parms(fp);
+}
+
+static int freq_domain_loopback(PHY_VARS_NR_UE *UE_tx, PHY_VARS_NR_UE *UE_rx, int frame, int slot, nr_phy_data_tx_t *phy_data)
+{
+ sl_nr_ue_phy_params_t *sl_ue1 = &UE_tx->SL_UE_PHY_PARAMS;
+ sl_nr_ue_phy_params_t *sl_ue2 = &UE_rx->SL_UE_PHY_PARAMS;
+
+ printf("\nPSBCH SIM -F: %d:%d slss id TX UE:%d, RX UE:%d\n",
+ frame,
+ slot,
+ phy_data->psbch_vars.tx_slss_id,
+ sl_ue2->sl_config.sl_sync_source.rx_slss_id);
+
+ NR_DL_FRAME_PARMS *fp = &sl_ue1->sl_frame_params;
+ const int samplesF_per_slot = NR_SYMBOLS_PER_SLOT * fp->ofdm_symbol_size;
+ c16_t txdataF_buf[fp->nb_antennas_tx * samplesF_per_slot] __attribute__((aligned(32)));
+ memset(txdataF_buf, 0, sizeof(txdataF_buf));
+ c16_t *txdataF[fp->nb_antennas_tx]; /* workaround to be compatible with current txdataF usage in all tx procedures. */
+ for (int i = 0; i < fp->nb_antennas_tx; ++i)
+ txdataF[i] = &txdataF_buf[i * samplesF_per_slot];
+
+ nr_tx_psbch(UE_tx, frame, slot, &phy_data->psbch_vars, txdataF);
+
+ int estimateSz = sl_ue2->sl_frame_params.samples_per_slot_wCP;
+ __attribute__((aligned(32))) struct complex16 rxdataF[1][estimateSz];
+ for (int i = 0; i < sl_ue1->sl_frame_params.samples_per_slot_wCP; i++) {
+ struct complex16 *txdataF_ptr = (struct complex16 *)&txdataF[0][i];
+ struct complex16 *rxdataF_ptr = (struct complex16 *)&rxdataF[0][i];
+ rxdataF_ptr->r = txdataF_ptr->r;
+ rxdataF_ptr->i = txdataF_ptr->i;
+ // printf("r,i TXDATAF[%d]- %d:%d, RXDATAF[%d]- %d:%d\n",
+ // i, txdataF_ptr->r, txdataF_ptr->i, i, txdataF_ptr->r, txdataF_ptr->i);
+ }
+
+ uint8_t err_status = 0;
+
+ UE_nr_rxtx_proc_t proc;
+ proc.frame_rx = frame;
+ proc.nr_slot_rx = slot;
+
+ struct complex16 dl_ch_estimates[1][estimateSz];
+ uint8_t decoded_output[4] = {0};
+
+ LOG_I(PHY, "DEBUG: HIJACKING DL CHANNEL ESTIMATES.\n");
+ for (int s = 0; s < 14; s++) {
+ for (int j = 0; j < sl_ue2->sl_frame_params.ofdm_symbol_size; j++) {
+ struct complex16 *dlch = (struct complex16 *)(&dl_ch_estimates[0][s * sl_ue2->sl_frame_params.ofdm_symbol_size]);
+ dlch[j].r = 128;
+ dlch[j].i = 0;
+ }
+ }
+
+ err_status = nr_rx_psbch(UE_rx,
+ &proc,
+ estimateSz,
+ dl_ch_estimates,
+ &sl_ue2->sl_frame_params,
+ decoded_output,
+ rxdataF,
+ sl_ue2->sl_config.sl_sync_source.rx_slss_id);
+
+ int error_payload = 0;
+ error_payload = test_rx_mib(decoded_output, frame, slot);
+
+ if (err_status == 0 || error_payload == 0) {
+ LOG_I(PHY, "---------PSBCH -F TEST OK.\n");
+ return 0;
+ }
+ LOG_E(PHY, "--------PSBCH -F TEST NOK. FAIL.\n");
+ return -1;
+}
+
+PHY_VARS_NR_UE *UE_TX; // for tx
+PHY_VARS_NR_UE *UE_RX; // for rx
+double cpuf;
+
+configmodule_interface_t *uniqCfg = NULL;
+int main(int argc, char **argv)
+{
+ char c;
+ int test_freqdomain_loopback = 0, test_slss_search = 0;
+ int frame = 5, slot = 10, frame_tx = 0, slot_tx = 0;
+ int loglvl = OAILOG_INFO;
+ uint16_t slss_id = 336, ssb_offset = 0;
+ double snr1 = 2.0, snr0 = 2.0, SNR;
+ double sigma2 = 0.0, sigma2_dB = 0.0;
+ double cfo = 0, ip = 0.0;
+
+ SCM_t channel_model = AWGN; // Rayleigh1_anticorr;
+ int N_RB_DL = 106, mu = 1;
+
+ uint16_t errors = 0, n_trials = 1;
+
+ int frame_length_complex_samples;
+ // int frame_length_complex_samples_no_prefix;
+ NR_DL_FRAME_PARMS *frame_parms;
+
+ int seed = 0;
+
+ cpuf = get_cpu_freq_GHz();
+
+ if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == 0) {
+ exit_fun("[NR_PSBCHSIM] Error, configuration module init failed\n");
+ }
+
+ randominit(0);
+
+ while ((c = getopt(argc, argv, "c:hn:o:s:FIL:N:R:S:T:")) != -1) {
+ printf("SIDELINK PSBCH SIM: handling optarg %c\n", c);
+ switch (c) {
+ case 'c':
+ cfo = atof(optarg);
+ printf("Setting CFO to %f Hz\n", cfo);
+ break;
+
+ case 'g':
+ switch ((char)*optarg) {
+ case 'A':
+ channel_model = SCM_A;
+ break;
+
+ case 'B':
+ channel_model = SCM_B;
+ break;
+
+ case 'C':
+ channel_model = SCM_C;
+ break;
+
+ case 'D':
+ channel_model = SCM_D;
+ break;
+
+ case 'E':
+ channel_model = EPA;
+ break;
+
+ case 'F':
+ channel_model = EVA;
+ break;
+
+ case 'G':
+ channel_model = ETU;
+ break;
+
+ default:
+ printf("Unsupported channel model! Exiting.\n");
+ exit(-1);
+ }
+ break;
+
+ case 'n':
+ n_trials = atoi(optarg);
+ break;
+
+ case 'o':
+ ssb_offset = atoi(optarg);
+ printf("SIDELINK PSBCH SIM: ssb offset from pointA:%d\n", ssb_offset);
+ break;
+
+ case 's':
+ slss_id = atoi(optarg);
+ printf("SIDELINK PSBCH SIM: slss_id from arg:%d\n", slss_id);
+ AssertFatal(slss_id >= 0 && slss_id <= 671, "SLSS ID not within Range 0-671\n");
+ break;
+
+ case 'F':
+ test_freqdomain_loopback = 1;
+ break;
+
+ case 'I':
+ test_slss_search = 1;
+ printf("SIDELINK PSBCH SIM: SLSS search will be tested\n");
+ break;
+
+ case 'L':
+ loglvl = atoi(optarg);
+ break;
+
+ case 'N':
+ snr0 = atoi(optarg);
+ snr1 = snr0;
+ printf("Setting SNR0 to %f. Test uses this SNR as target SNR\n", snr0);
+ break;
+
+ case 'R':
+ N_RB_DL = atoi(optarg);
+ printf("SIDELINK PSBCH SIM: N_RB_DL:%d\n", N_RB_DL);
+ break;
+
+ case 'S':
+ snr1 = atof(optarg);
+ printf("Setting SNR1 to %f. Test will run until this SNR as target SNR\n", snr1);
+ AssertFatal(snr1 <= snr0, "Test runs SNR down, set snr1 to a lower value than %f\n", snr0);
+ break;
+
+ case 'T':
+ frame = atoi(optarg);
+ slot = atoi(argv[optind]);
+ printf("PSBCH SIM: frame timing- %d:%d\n", frame, slot);
+ break;
+
+ case 'h':
+ default:
+ printf("\n\nSIDELINK PSBCH SIM OPTIONS LIST - hus:FL:T:\n");
+ printf("-h: HELP\n");
+ printf("-c Carrier frequency offset in Hz\n");
+ printf("-n Number of trials\n");
+ printf("-o ssb offset from PointA - indicates ssb_start subcarrier\n");
+ printf("-s: set Sidelink sync id slss_id. ex -s 100\n");
+ printf("-F: Run PSBCH frequency domain loopback test of the samples\n");
+ printf("-I: Sidelink SLSS search will be tested.\n");
+ printf("-L: Set Log Level.\n");
+ printf("-N: Test with Noise. target SNR0 eg -N 10\n");
+ printf("-R N_RB_DL\n");
+ printf("-S Ending SNR, runs from SNR0 to SNR1\n");
+ printf("-T: Frame,Slot to be sent in sl-MIB eg -T 4 2\n");
+ return 1;
+ }
+ }
+
+ randominit(seed);
+
+ logInit();
+ set_glog(loglvl);
+
+ double fs = 0, eps;
+ double scs = 30000;
+ double bw = 100e6;
+
+ switch (mu) {
+ case 1:
+ scs = 30000;
+ if (N_RB_DL == 217) {
+ fs = 122.88e6;
+ bw = 80e6;
+ } else if (N_RB_DL == 245) {
+ fs = 122.88e6;
+ bw = 90e6;
+ } else if (N_RB_DL == 273) {
+ fs = 122.88e6;
+ bw = 100e6;
+ } else if (N_RB_DL == 106) {
+ fs = 61.44e6;
+ bw = 40e6;
+ } else
+ AssertFatal(1 == 0, "Unsupported numerology for mu %d, N_RB %d\n", mu, N_RB_DL);
+ break;
+ case 3:
+ scs = 120000;
+ if (N_RB_DL == 66) {
+ fs = 122.88e6;
+ bw = 100e6;
+ } else
+ AssertFatal(1 == 0, "Unsupported numerology for mu %d, N_RB %d\n", mu, N_RB_DL);
+ break;
+ }
+
+ // cfo with respect to sub-carrier spacing
+ eps = cfo / scs;
+
+ // computation of integer and fractional FO to compare with estimation results
+ int IFO;
+ if (eps != 0.0) {
+ printf("Introducing a CFO of %lf relative to SCS of %d kHz\n", eps, (int)(scs / 1000));
+ if (eps > 0)
+ IFO = (int)(eps + 0.5);
+ else
+ IFO = (int)(eps - 0.5);
+ printf("FFO = %lf; IFO = %d\n", eps - IFO, IFO);
+ }
+
+ channel_desc_t *UE2UE;
+ int n_tx = 1, n_rx = 1;
+ UE2UE = new_channel_desc_scm(n_tx, n_rx, channel_model, fs, 0, bw, 300e-9, 0.0, CORR_LEVEL_LOW, 0, 0, 0, 0);
+
+ if (UE2UE == NULL) {
+ printf("Problem generating channel model. Exiting.\n");
+ exit(-1);
+ }
+
+ /*****configure UE *************************/
+ UE_TX = calloc(1, sizeof(PHY_VARS_NR_UE));
+ UE_RX = calloc(1, sizeof(PHY_VARS_NR_UE));
+ LOG_I(PHY, "Configure UE-TX and sidelink UE-TX.\n");
+ configure_NR_UE(UE_TX, mu, N_RB_DL);
+ configure_SL_UE(UE_TX, mu, N_RB_DL, ssb_offset, 0xFFFF);
+
+ LOG_I(PHY, "Configure UE-RX and sidelink UE-RX.\n");
+ configure_NR_UE(UE_RX, mu, N_RB_DL);
+ UE_RX->is_synchronized = (test_slss_search) ? 0 : 1;
+ configure_SL_UE(UE_RX, mu, N_RB_DL, ssb_offset, slss_id);
+ /*****************************************/
+ sl_nr_ue_phy_params_t *sl_uetx = &UE_TX->SL_UE_PHY_PARAMS;
+ sl_nr_ue_phy_params_t *sl_uerx = &UE_RX->SL_UE_PHY_PARAMS;
+ frame_parms = &sl_uetx->sl_frame_params;
+ frame_tx = frame % 1024;
+ slot_tx = slot % frame_parms->slots_per_frame;
+
+ frame_length_complex_samples = frame_parms->samples_per_subframe * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
+ // frame_length_complex_samples_no_prefix = frame_parms->samples_per_subframe_wCP;
+
+ double **s_re, **s_im, **r_re, **r_im;
+ s_re = malloc(2 * sizeof(double *));
+ s_im = malloc(2 * sizeof(double *));
+ r_re = malloc(2 * sizeof(double *));
+ r_im = malloc(2 * sizeof(double *));
+
+ s_re[0] = malloc16_clear(frame_length_complex_samples * sizeof(double));
+ s_im[0] = malloc16_clear(frame_length_complex_samples * sizeof(double));
+ r_re[0] = malloc16_clear(frame_length_complex_samples * sizeof(double));
+ r_im[0] = malloc16_clear(frame_length_complex_samples * sizeof(double));
+
+ if (eps != 0.0)
+ UE_RX->UE_fo_compensation = 1; // if a frequency offset is set then perform fo estimation and compensation
+
+ UE_nr_rxtx_proc_t proc;
+ proc.frame_tx = frame;
+ proc.nr_slot_tx = slot;
+ nr_phy_data_tx_t phy_data_tx;
+ phy_data_tx.psbch_vars.tx_slss_id = slss_id;
+
+ uint8_t sl_mib[4] = {0};
+ prepare_mib_bits(sl_mib, frame, slot);
+ memcpy(phy_data_tx.psbch_vars.psbch_payload, sl_mib, 4);
+
+ phy_data_tx.sl_tx_action = SL_NR_CONFIG_TYPE_TX_PSBCH;
+ proc.frame_rx = frame;
+ proc.nr_slot_rx = slot;
+ nr_phy_data_t phy_data_rx;
+ phy_data_rx.sl_rx_action = SL_NR_CONFIG_TYPE_RX_PSBCH;
+
+ if (test_freqdomain_loopback) {
+ errors += freq_domain_loopback(UE_TX, UE_RX, frame_tx, slot_tx, &phy_data_tx);
+ }
+
+ printf("\nSidelink TX UE - Frame.Slot %d.%d SLSS id:%d\n", frame, slot, phy_data_tx.psbch_vars.tx_slss_id);
+ printf("Sidelink RX UE - Frame.Slot %d.%d SLSS id:%d\n",
+ proc.frame_rx,
+ proc.nr_slot_rx,
+ sl_uerx->sl_config.sl_sync_source.rx_slss_id);
+
+ phy_procedures_nrUE_SL_TX(UE_TX, &proc, &phy_data_tx);
+
+ for (SNR = snr0; SNR >= snr1; SNR -= 1) {
+ for (int trial = 0; trial < n_trials; trial++) {
+ for (int i = 0; i < frame_length_complex_samples; i++) {
+ for (int aa = 0; aa < frame_parms->nb_antennas_tx; aa++) {
+ struct complex16 *txdata_ptr = (struct complex16 *)&UE_TX->common_vars.txData[aa][i];
+ r_re[aa][i] = (double)txdata_ptr->r;
+ r_im[aa][i] = (double)txdata_ptr->i;
+ }
+ }
+
+ LOG_M("txData0.m", "txd0", UE_TX->common_vars.txData[0], frame_parms->samples_per_frame, 1, 1);
+
+ // AWGN
+ sigma2_dB = 20 * log10((double)AMP / 4) - SNR;
+ sigma2 = pow(10, sigma2_dB / 10);
+ // printf("sigma2 %f (%f dB), tx_lev %f (%f dB)\n",sigma2,sigma2_dB,txlev,10*log10((double)txlev));
+
+ if (eps != 0.0) {
+ rf_rx(r_re, // real part of txdata
+ r_im, // imag part of txdata
+ NULL, // interference real part
+ NULL, // interference imag part
+ 0, // interference power
+ frame_parms->nb_antennas_rx, // number of rx antennas
+ frame_length_complex_samples, // number of samples in frame
+ 1.0e9 / fs, // sampling time (ns)
+ cfo, // frequency offset in Hz
+ 0.0, // drift (not implemented)
+ 0.0, // noise figure (not implemented)
+ 0.0, // rx gain in dB ?
+ 200, // 3rd order non-linearity in dB ?
+ &ip, // initial phase
+ 30.0e3, // phase noise cutoff in kHz
+ -500.0, // phase noise amplitude in dBc
+ 0.0, // IQ imbalance (dB),
+ 0.0); // IQ phase imbalance (rad)
+ }
+
+ for (int i = 0; i < frame_length_complex_samples; i++) {
+ for (int aa = 0; aa < frame_parms->nb_antennas_rx; aa++) {
+ UE_RX->common_vars.rxdata[aa][i].r = (short)(r_re[aa][i] + sqrt(sigma2 / 2) * gaussdouble(0.0, 1.0));
+ UE_RX->common_vars.rxdata[aa][i].i = (short)(r_im[aa][i] + sqrt(sigma2 / 2) * gaussdouble(0.0, 1.0));
+ }
+ }
+
+ if (UE_RX->is_synchronized == 0) {
+ nr_initial_sync_t ret = {false, 0};
+ UE_nr_rxtx_proc_t proc = {0};
+ // Should not have SLSS id configured. Search should find SLSS id from TX UE
+ UE_RX->SL_UE_PHY_PARAMS.sl_config.sl_sync_source.rx_slss_id = 0xFFFF;
+ ret = sl_nr_slss_search(UE_RX, &proc, 1);
+ printf("Sidelink SLSS search returns status:%d, rx_offset:%d\n", ret.cell_detected, ret.rx_offset);
+ if (!ret.cell_detected)
+ sl_uerx->psbch.rx_errors = 1;
+ else {
+ AssertFatal(UE_RX->SL_UE_PHY_PARAMS.sync_params.N_sl_id == slss_id,
+ "DETECTED INCORRECT SLSS ID in SEARCH.CHECK id:%d\n",
+ UE_RX->SL_UE_PHY_PARAMS.sync_params.N_sl_id);
+ sl_uerx->psbch.rx_ok = 1;
+ }
+ } else
+ psbch_pscch_processing(UE_RX, &proc, &phy_data_rx);
+
+ } // noise trials
+
+ printf("Runs:%d SNR %f: SLSS Search:%d crc ERRORs = %d, OK = %d\n",
+ n_trials,
+ SNR,
+ !UE_RX->is_synchronized,
+ sl_uerx->psbch.rx_errors,
+ sl_uerx->psbch.rx_ok);
+ errors += sl_uerx->psbch.rx_errors;
+ sl_uerx->psbch.rx_errors = 0;
+ sl_uerx->psbch.rx_ok = 0;
+
+ } // NSR
+
+ if (errors == 0)
+ LOG_I(PHY, "PSBCH test OK\n");
+ else
+ LOG_E(PHY, "PSBCH test NOT OK\n");
+
+ free_channel_desc_scm(UE2UE);
+
+ free(s_re[0]);
+ free(s_im[0]);
+ free(r_re[0]);
+ free(r_im[0]);
+ free(s_re);
+ free(s_im);
+ free(r_re);
+ free(r_im);
+
+ term_nr_ue_signal(UE_TX, 1);
+ term_nr_ue_signal(UE_RX, 1);
+
+ free(UE_TX);
+ free(UE_RX);
+ logTerm();
+ loader_reset();
+
+ return errors;
+}
diff --git a/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.h b/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.h
index 9acb4785fd..5cca5b0f80 100644
--- a/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.h
+++ b/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.h
@@ -98,7 +98,7 @@ typedef struct {
uint32_t gNB_index;
/// component carrier id
int cc_id;
- /// frame
+ /// frame rx
frame_t frame_rx;
/// slot rx
uint32_t slot_rx;