mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
Compare commits
7 Commits
4c6747951d
...
1d823735c6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d823735c6 | ||
|
|
c4125d1c74 | ||
|
|
12ef1ccf85 | ||
|
|
bfe0b95df5 | ||
|
|
d633682773 | ||
|
|
ec4544992e | ||
|
|
3d4b18a1b4 |
123
ci-scripts/xml_files/container_5g_zmq_ocudu_trs.xml
Normal file
123
ci-scripts/xml_files/container_5g_zmq_ocudu_trs.xml
Normal file
@@ -0,0 +1,123 @@
|
||||
<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->
|
||||
|
||||
<testCaseList>
|
||||
<htmlTabRef>ocudu-5gnr-tdd-trs</htmlTabRef>
|
||||
<htmlTabName>OCUDU gnb split8 with OAI UE</htmlTabName>
|
||||
<htmlTabIcon>wrench</htmlTabIcon>
|
||||
|
||||
<testCase>
|
||||
<class>Pull_Local_Registry</class>
|
||||
<desc>Pull Images from Local Registry</desc>
|
||||
<node>localhost</node>
|
||||
<images>oai-gnb-asan oai-nr-ue-asan</images>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Create_Workspace</class>
|
||||
<desc>Create new Workspace</desc>
|
||||
<node>localhost</node>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Deploy_Object</class>
|
||||
<desc>Deploy OAI 5G CoreNetwork</desc>
|
||||
<node>localhost</node>
|
||||
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_trs</yaml_path>
|
||||
<services>mysql oai-amf oai-smf oai-upf oai-ext-dn</services>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Deploy_Object</class>
|
||||
<desc>Deploy OCUDU Split 8 gNB + GNURadio + OAI UE</desc>
|
||||
<node>localhost</node>
|
||||
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_trs</yaml_path>
|
||||
<services>ocudu-gnb gnuradio-zmq oai-nr-ue</services>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Attach_UE</class>
|
||||
<desc>Attach OAI UE (Wait for IP)</desc>
|
||||
<id>rfsim5g_ue</id>
|
||||
<node>localhost</node>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Simulate frequency drift of 50 Hz per second for 13 seconds</desc>
|
||||
<node>localhost</node>
|
||||
<command>docker exec gnuradio-zmq python3 /app/send_freq_offset_zmq.py --num-msg 13</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Verify TRS frequency correction</desc>
|
||||
<node>localhost</node>
|
||||
<command>timeout 3 docker logs -f oai-nr-ue 2>&1 | grep -m1 'CFO estimated \([5-6][0-9][0-9]\) from TRS exceeded threshold'</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Ping</class>
|
||||
<desc>Ping ext-dn from NR-UE</desc>
|
||||
<id>rfsim5g_ue</id>
|
||||
<node>localhost</node>
|
||||
<svr_id>rfsim5g_ext_dn</svr_id>
|
||||
<svr_node>localhost</svr_node>
|
||||
<ping_args>-c 20 -i 0.25</ping_args>
|
||||
<ping_packetloss_threshold>5</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Ping</class>
|
||||
<desc>Ping NR-UE from ext-dn</desc>
|
||||
<id>rfsim5g_ext_dn</id>
|
||||
<node>localhost</node>
|
||||
<svr_id>rfsim5g_ue</svr_id>
|
||||
<svr_node>localhost</svr_node>
|
||||
<ping_args>-c 20 -i 0.25</ping_args>
|
||||
<ping_packetloss_threshold>5</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Iperf</class>
|
||||
<desc>Iperf (DL/3Mbps/UDP)(20 sec)</desc>
|
||||
<iperf_args>-u -b 3M -t 20 -R</iperf_args>
|
||||
<id>rfsim5g_ue</id>
|
||||
<node>localhost</node>
|
||||
<svr_id>rfsim5g_ext_dn</svr_id>
|
||||
<svr_node>localhost</svr_node>
|
||||
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
|
||||
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Iperf</class>
|
||||
<desc>Iperf (UL/3Mbps/UDP)(20 sec)</desc>
|
||||
<iperf_args>-u -b 3M -t 20</iperf_args>
|
||||
<id>rfsim5g_ue</id>
|
||||
<node>localhost</node>
|
||||
<svr_id>rfsim5g_ext_dn</svr_id>
|
||||
<svr_node>localhost</svr_node>
|
||||
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
|
||||
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Undeploy_Object</class>
|
||||
<always_exec>true</always_exec>
|
||||
<desc>Undeploy all OAI 5G stack</desc>
|
||||
<node>localhost</node>
|
||||
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_trs</yaml_path>
|
||||
<analysis>
|
||||
<services>oai-nr-ue</services>
|
||||
</analysis>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Clean_Test_Server_Images</class>
|
||||
<always_exec>true</always_exec>
|
||||
<desc>Clean Test Images on Test Server</desc>
|
||||
<node>localhost</node>
|
||||
<images>oai-gnb-asan oai-nr-ue-asan</images>
|
||||
</testCase>
|
||||
|
||||
</testCaseList>
|
||||
11
ci-scripts/yaml_files/5g_zmq_radio_trs/Dockerfile.gnuradio
Normal file
11
ci-scripts/yaml_files/5g_zmq_radio_trs/Dockerfile.gnuradio
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gnuradio \
|
||||
python3-zmq \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
COPY rotate_zmq.py send_freq_offset_zmq.py .
|
||||
178
ci-scripts/yaml_files/5g_zmq_radio_trs/docker-compose.yaml
Normal file
178
ci-scripts/yaml_files/5g_zmq_radio_trs/docker-compose.yaml
Normal file
@@ -0,0 +1,178 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
services:
|
||||
mysql:
|
||||
container_name: "rfsim5g-mysql"
|
||||
image: mysql:9.6
|
||||
init: true
|
||||
volumes:
|
||||
- ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql
|
||||
- ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh
|
||||
environment:
|
||||
- TZ=Europe/Paris
|
||||
- MYSQL_DATABASE=oai_db
|
||||
- MYSQL_USER=test
|
||||
- MYSQL_PASSWORD=test
|
||||
- MYSQL_ROOT_PASSWORD=linux
|
||||
healthcheck:
|
||||
test: /bin/bash -c "/tmp/mysql-healthcheck.sh"
|
||||
start_period: 10s
|
||||
start_interval: 500ms
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.131
|
||||
oai-amf:
|
||||
container_name: "rfsim5g-oai-amf"
|
||||
image: oaisoftwarealliance/oai-amf:v2.2.1
|
||||
environment:
|
||||
- TZ=Europe/paris
|
||||
volumes:
|
||||
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-amf/etc/config.yaml
|
||||
depends_on:
|
||||
- mysql
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.132
|
||||
oai-smf:
|
||||
container_name: "rfsim5g-oai-smf"
|
||||
image: oaisoftwarealliance/oai-smf:v2.2.1
|
||||
environment:
|
||||
- TZ=Europe/Paris
|
||||
volumes:
|
||||
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml
|
||||
depends_on:
|
||||
- oai-amf
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.133
|
||||
oai-upf:
|
||||
container_name: "rfsim5g-oai-upf"
|
||||
image: oaisoftwarealliance/oai-upf:v2.2.1
|
||||
init: true
|
||||
environment:
|
||||
- TZ=Europe/Paris
|
||||
volumes:
|
||||
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-upf/etc/config.yaml
|
||||
depends_on:
|
||||
- oai-smf
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_ADMIN
|
||||
cap_drop:
|
||||
- ALL
|
||||
privileged: true
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.134
|
||||
interface_name: eth0
|
||||
traffic_net:
|
||||
ipv4_address: 192.168.72.134
|
||||
interface_name: eth1
|
||||
oai-ext-dn:
|
||||
privileged: true
|
||||
container_name: rfsim5g-oai-ext-dn
|
||||
image: oaisoftwarealliance/trf-gen-cn5g:latest
|
||||
init: true
|
||||
entrypoint: /bin/bash -c \
|
||||
"iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\
|
||||
"ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity"
|
||||
depends_on:
|
||||
- oai-upf
|
||||
networks:
|
||||
traffic_net:
|
||||
ipv4_address: 192.168.72.135
|
||||
healthcheck:
|
||||
test: /bin/bash -c "ping -c 2 192.168.72.134"
|
||||
start_period: 10s
|
||||
start_interval: 500ms
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
ocudu-gnb:
|
||||
image: ghcr.io/bpodrygajlo/ocudu/ocudu-avx2:7cecca5
|
||||
container_name: ocudu-gnb
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- SYS_NICE
|
||||
depends_on:
|
||||
- oai-ext-dn
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.140
|
||||
volumes:
|
||||
- ./ocudu.yml:/ocudu.yml
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep gnb_split_8"
|
||||
start_period: 10s
|
||||
start_interval: 500ms
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
command: ./usr/local/bin/gnb_split_8 -c /ocudu.yml
|
||||
|
||||
gnuradio-zmq:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.gnuradio
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- SYS_NICE
|
||||
depends_on:
|
||||
- ocudu-gnb
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.141
|
||||
command:
|
||||
bash -c "cd /app/ & python3 rotate_zmq.py --zmq-req-src tcp://192.168.71.140:4558 --zmq-req-snk tcp://0.0.0.0:4559"
|
||||
|
||||
oai-nr-ue:
|
||||
image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
|
||||
container_name: rfsim5g-oai-nr-ue
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- NET_ADMIN # for interface bringup
|
||||
- NET_RAW # for ping
|
||||
- SYS_NICE
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: -r 51 --numerology 1 --band 78 -C 3489420000 --ssb 0 -E --uicc0.imsi 208990100001100 --uecap_file /opt/oai-nr-ue/etc/uecap.xml --log_config.global_log_options level,nocolor,time --device.name oai_zmqdevif --zmq.[0].tx_channels tcp://0.0.0.0:4556 --zmq.[0].rx_channels tcp://192.168.71.141:4559
|
||||
depends_on:
|
||||
- gnuradio-zmq
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.150
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
volumes:
|
||||
- ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports1.xml:/opt/oai-nr-ue/etc/uecap.xml
|
||||
- ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-uesoftmodem"
|
||||
start_period: 10s
|
||||
start_interval: 500ms
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
networks:
|
||||
public_net:
|
||||
driver: bridge
|
||||
name: rfsim5g-oai-public-net
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 192.168.71.128/26
|
||||
driver_opts:
|
||||
com.docker.network.bridge.name: "rfsim5g-public"
|
||||
traffic_net:
|
||||
driver: bridge
|
||||
name: rfsim5g-oai-traffic-net
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 192.168.72.128/26
|
||||
driver_opts:
|
||||
com.docker.network.bridge.name: "rfsim5g-traffic"
|
||||
52
ci-scripts/yaml_files/5g_zmq_radio_trs/ocudu.yml
Normal file
52
ci-scripts/yaml_files/5g_zmq_radio_trs/ocudu.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
# SPDX-FileCopyrightText: Copyright (C) 2021-2026 Software Radio Systems Limited
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Open-MPI
|
||||
# Refer to https://gitlab.com/ocudu/ocudu/-/raw/dev/LICENSE?ref_type=heads
|
||||
|
||||
cu_cp:
|
||||
amf:
|
||||
addrs: 192.168.71.132
|
||||
port: 38412
|
||||
bind_addrs: 192.168.71.140
|
||||
supported_tracking_areas:
|
||||
- tac: 1
|
||||
plmn_list:
|
||||
- plmn: "20899"
|
||||
tai_slice_support_list:
|
||||
- sst: 1
|
||||
|
||||
ru_sdr:
|
||||
device_driver: zmq
|
||||
device_args: tx_port=tcp://0.0.0.0:4558,rx_port=tcp://192.168.71.150:4556
|
||||
srate: 23.04
|
||||
tx_gain: 25
|
||||
rx_gain: 25
|
||||
|
||||
cell_cfg:
|
||||
dl_arfcn: 632628
|
||||
band: 78
|
||||
channel_bandwidth_MHz: 20
|
||||
common_scs: 30
|
||||
plmn: "20899"
|
||||
tac: 1
|
||||
pci: 1
|
||||
tdd_ul_dl_cfg:
|
||||
dl_ul_tx_period: 5
|
||||
nof_dl_slots: 3
|
||||
nof_dl_symbols: 10
|
||||
nof_ul_slots: 1
|
||||
nof_ul_symbols: 2
|
||||
csi:
|
||||
csi_rs_enabled: false
|
||||
pucch:
|
||||
formats: f0_and_f2
|
||||
nof_cell_csi_res: 0
|
||||
|
||||
log:
|
||||
filename: gnb.log
|
||||
all_level: debug
|
||||
|
||||
pcap:
|
||||
mac_enable: false
|
||||
mac_filename: /tmp/gnb_mac.pcap
|
||||
ngap_enable: false
|
||||
ngap_filename: /tmp/gnb_ngap.pcap
|
||||
216
ci-scripts/yaml_files/5g_zmq_radio_trs/rotate_zmq.py
Normal file
216
ci-scripts/yaml_files/5g_zmq_radio_trs/rotate_zmq.py
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0
|
||||
#
|
||||
# GNU Radio Python Flow Graph
|
||||
# Title: Simple doppler injector
|
||||
# GNU Radio version: 3.10.9.2
|
||||
|
||||
import threading
|
||||
import struct
|
||||
import sys
|
||||
import signal
|
||||
from argparse import ArgumentParser
|
||||
|
||||
import zmq
|
||||
|
||||
from gnuradio import analog, blocks, gr
|
||||
from gnuradio.filter import firdes
|
||||
from gnuradio.fft import window
|
||||
from gnuradio import zeromq
|
||||
|
||||
|
||||
class untitled(gr.top_block):
|
||||
|
||||
def __init__(self, samp_rate=23040000, rx_freq=0, freq_off=0, zmq_req_src='tcp://127.0.0.1:4445', zmq_req_snk='tcp://127.0.0.1:4446', zmq_pull_addr='tcp://*:5555', zmq_pull_oai_addr='tcp://*5556'):
|
||||
gr.top_block.__init__(self, "Simple doppler injector", catch_exceptions=True)
|
||||
|
||||
##################################################
|
||||
# Variables
|
||||
##################################################
|
||||
self.samp_rate = samp_rate
|
||||
self.freq_off = freq_off
|
||||
self.rx_freq = rx_freq
|
||||
|
||||
##################################################
|
||||
# Blocks
|
||||
##################################################
|
||||
|
||||
self.zeromq_req_source_0 = zeromq.req_source(gr.sizeof_gr_complex, 1, zmq_req_src, 100, False, (-1), False)
|
||||
self.zeromq_rep_sink_0 = zeromq.rep_sink(gr.sizeof_gr_complex, 1, zmq_req_snk, 100, False, (-1), True)
|
||||
self.blocks_throttle2_0_0 = blocks.throttle(
|
||||
gr.sizeof_gr_complex*1, samp_rate, True,
|
||||
0 if "auto" == "auto" else max(
|
||||
int(float(0.1) * samp_rate) if "auto" == "time" else int(0.1), 1)
|
||||
)
|
||||
self.blocks_multiply_xx_0 = blocks.multiply_vcc(1)
|
||||
self.analog_sig_source_x_0_1_0 = analog.sig_source_c(samp_rate, analog.GR_COS_WAVE, freq_off, 1, 0, 0)
|
||||
|
||||
##################################################
|
||||
# Connections
|
||||
##################################################
|
||||
self.connect((self.analog_sig_source_x_0_1_0, 0), (self.blocks_multiply_xx_0, 0))
|
||||
self.connect((self.blocks_multiply_xx_0, 0), (self.zeromq_rep_sink_0, 0))
|
||||
self.connect((self.blocks_throttle2_0_0, 0), (self.blocks_multiply_xx_0, 1))
|
||||
self.connect((self.zeromq_req_source_0, 0), (self.blocks_throttle2_0_0, 0))
|
||||
|
||||
##################################################
|
||||
# ZMQ sockets
|
||||
##################################################
|
||||
self._zmq_ctx = zmq.Context()
|
||||
self._zmq_ctx_oai = zmq.Context()
|
||||
|
||||
# PULL - receive freq offset updates from Python script
|
||||
self._pull_sock = self._zmq_ctx.socket(zmq.PULL)
|
||||
self._pull_sock.setsockopt(zmq.LINGER, 0)
|
||||
self._pull_sock.bind(zmq_pull_addr)
|
||||
print(f"[zmq] PULL bound to {zmq_pull_addr}")
|
||||
|
||||
# PULL - receive carrier freq updates from OAI
|
||||
self._pull_sock_oai = self._zmq_ctx_oai.socket(zmq.PULL)
|
||||
self._pull_sock_oai.setsockopt(zmq.LINGER, 0)
|
||||
self._pull_sock_oai.bind(zmq_pull_oai_addr)
|
||||
print(f"[zmq] PULL bound to {zmq_pull_oai_addr}")
|
||||
|
||||
self._zmq_thread = threading.Thread(target=self._zmq_listener, daemon=True)
|
||||
self._zmq_thread_oai = threading.Thread(target=self._zmq_listener_oai, daemon=True)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Getters / setters
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def get_samp_rate(self):
|
||||
return self.samp_rate
|
||||
|
||||
def get_rx_freq(self):
|
||||
return self.rx_freq
|
||||
|
||||
def set_rx_freq(self, rx_freq):
|
||||
self.rx_freq = rx_freq
|
||||
print(f"[rx_freq] -> {self.rx_freq}")
|
||||
|
||||
def set_samp_rate(self, samp_rate):
|
||||
self.samp_rate = samp_rate
|
||||
self.analog_sig_source_x_0_1_0.set_sampling_freq(self.samp_rate)
|
||||
self.blocks_throttle2_0_0.set_sample_rate(self.samp_rate)
|
||||
print(f"[samp_rate] -> {self.samp_rate}")
|
||||
|
||||
def get_freq_off(self):
|
||||
return self.freq_off
|
||||
|
||||
def set_freq_off(self, freq_off):
|
||||
self.freq_off = freq_off
|
||||
self.analog_sig_source_x_0_1_0.set_frequency(self.freq_off)
|
||||
print(f"[freq_off] -> {self.freq_off}")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# ZMQ PULL listener
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _zmq_listener(self):
|
||||
while True:
|
||||
try:
|
||||
# Accept either a raw 8-byte double or a UTF-8 string
|
||||
raw = self._pull_sock.recv()
|
||||
if len(raw) == 8:
|
||||
val = struct.unpack('d', raw)[0] # double from C/numpy sender
|
||||
else:
|
||||
val = float(raw.decode('utf-8').strip())
|
||||
self.set_freq_off(self.freq_off + val)
|
||||
except (ValueError, struct.error) as e:
|
||||
print(f"[zmq] invalid message: {e}")
|
||||
except zmq.ZMQError as e:
|
||||
print(f"[zmq] error: {e}")
|
||||
break
|
||||
|
||||
def _zmq_listener_oai(self):
|
||||
while True:
|
||||
try:
|
||||
# Accept either a raw 8-byte double or a UTF-8 string
|
||||
raw = self._pull_sock_oai.recv()
|
||||
if len(raw) == 8:
|
||||
val = struct.unpack('d', raw)[0] # double from C/numpy sender
|
||||
else:
|
||||
val = float(raw.decode('utf-8').strip())
|
||||
self.set_rx_freq(val)
|
||||
self.set_freq_off(0)
|
||||
except (ValueError, struct.error) as e:
|
||||
print(f"[zmq] invalid message: {e}")
|
||||
except zmq.ZMQError as e:
|
||||
print(f"[zmq] error: {e}")
|
||||
break
|
||||
|
||||
def start(self):
|
||||
self._zmq_thread.start()
|
||||
self._zmq_thread_oai.start()
|
||||
super().start()
|
||||
|
||||
def main(top_block_cls=untitled, options=None):
|
||||
parser = ArgumentParser(description="Simple doppler injector")
|
||||
parser.add_argument(
|
||||
"--samp-rate", type=int, default=23040000,
|
||||
help="Sample rate in Hz (default: 23040000)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rx-freq", type=int, default=3619200000,
|
||||
help="Carrier freq in Hz (default: 3619200000)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--freq-off", type=float, default=0.0,
|
||||
help="Initial frequency offset in Hz (default: 0.0)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zmq-req-src", type=str, default="tcp://127.0.0.1:4445",
|
||||
help="ZMQ REQ source address (default: tcp://127.0.0.1:4445)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zmq-req-snk", type=str, default="tcp://127.0.0.1:4446",
|
||||
help="ZMQ REQ sink address (default: tcp://127.0.0.1:4446)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zmq-pull-addr", type=str, default="tcp://*:5555",
|
||||
help="ZMQ PULL bind address for incoming freq_off updates (default: tcp://*:5555)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zmq-pull-oai-addr", type=str, default="tcp://*:5556",
|
||||
help="ZMQ PULL bind address for incoming carrier_freq updates from OAI UE (default: tcp://*:5556)"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
tb = top_block_cls(
|
||||
samp_rate=args.samp_rate,
|
||||
rx_freq=args.rx_freq,
|
||||
freq_off=args.freq_off,
|
||||
zmq_req_src=args.zmq_req_src,
|
||||
zmq_req_snk=args.zmq_req_snk,
|
||||
zmq_pull_addr=args.zmq_pull_addr,
|
||||
zmq_pull_oai_addr=args.zmq_pull_oai_addr,
|
||||
)
|
||||
|
||||
def sig_handler(sig=None, frame=None):
|
||||
tb.stop()
|
||||
tb.wait()
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, sig_handler)
|
||||
signal.signal(signal.SIGTERM, sig_handler)
|
||||
|
||||
tb.start()
|
||||
print(f"Flowgraph running: samp_rate={args.samp_rate}, rx_freq={args.rx_freq}")
|
||||
print(f"PULL (freq_off in): {args.zmq_pull_addr}")
|
||||
print(f"PULL (carrier_freq in): {args.zmq_pull_oai_addr}")
|
||||
|
||||
try:
|
||||
input('Press Enter to quit: ')
|
||||
except EOFError:
|
||||
pass
|
||||
|
||||
tb.stop()
|
||||
tb.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Send a freq offset to the GNURadio doppler injector every second via ZMQ PUSH.
|
||||
|
||||
Usage:
|
||||
python3 send_freq_offset_zmq.py --carrier-freq 3489420000 --freq-off 50.0 --interval 0.5 --addr tcp://127.0.0.1:5555
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
import struct
|
||||
import signal
|
||||
import threading
|
||||
import argparse
|
||||
|
||||
import zmq
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Send freq offset to GNURadio via ZMQ PUSH")
|
||||
parser.add_argument(
|
||||
"--freq-off", type=float, default=50.0,
|
||||
help="Frequency offset in Hz to send (e.g. 50.0)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--addr", type=str, default="tcp://127.0.0.1:5555",
|
||||
help="GNURadio PULL address to send freq_off to (default: tcp://127.0.0.1:5555)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--interval", type=float, default=1.0,
|
||||
help="Send interval in seconds (default: 1.0)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--num-msg", type=int, default=15,
|
||||
help="Number of freq offsets to send (default: 15)"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
ctx = zmq.Context()
|
||||
# PUSH - send freq_off to GNURadio PULL
|
||||
push_sock = ctx.socket(zmq.PUSH)
|
||||
push_sock.setsockopt(zmq.LINGER, 0)
|
||||
push_sock.connect(args.addr)
|
||||
print(f"[zmq] PUSH connected to {args.addr}")
|
||||
|
||||
def sig_handler(sig, frame):
|
||||
print("\nStopping.")
|
||||
push_sock.close()
|
||||
ctx.term()
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, sig_handler)
|
||||
signal.signal(signal.SIGTERM, sig_handler)
|
||||
|
||||
print(f"Sending freq_off = {args.freq_off} Hz every {args.interval}s - Ctrl+C to stop")
|
||||
|
||||
msg_count = 0
|
||||
while msg_count < args.num_msg:
|
||||
push_sock.send(struct.pack('d', args.freq_off))
|
||||
print(f"[sent] freq_offset = {args.freq_off} Hz")
|
||||
time.sleep(args.interval)
|
||||
msg_count += 1
|
||||
|
||||
print("\nStopping.")
|
||||
push_sock.close()
|
||||
ctx.term()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,23 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "common/config/config_userapi.h"
|
||||
#include "common/utils/system.h"
|
||||
#include "nr-oru.h"
|
||||
|
||||
@@ -1,23 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#ifndef __NR_ORU_H__
|
||||
#define __NR_ORU_H__
|
||||
#include "openair1/PHY/defs_RU.h"
|
||||
|
||||
@@ -215,9 +215,8 @@ static void UE_synch(void *arg) {
|
||||
((ret.rx_offset << 1) / fp->samples_per_subframe * fp->slots_per_subframe)
|
||||
+ round((float)((ret.rx_offset << 1) % fp->samples_per_subframe) / fp->samples_per_slot0);
|
||||
|
||||
if (get_nrUE_params()->cont_fo_comp) {
|
||||
UE->freq_offset = freq_offset - UE->dl_Doppler_shift;
|
||||
} else {
|
||||
if (!get_nrUE_params()->cont_fo_comp) {
|
||||
// rerun with new cell parameters and frequency-offset
|
||||
nrue_ru_set_freq(UE, ul_carrier, dl_carrier, freq_offset);
|
||||
}
|
||||
@@ -711,6 +710,18 @@ static inline int get_readBlockSize(uint16_t slot, const NR_DL_FRAME_PARMS *fp)
|
||||
return rem_samples + next_slot_first_symbol;
|
||||
}
|
||||
|
||||
void trs_freq_correction(PHY_VARS_NR_UE *ue, int cfo)
|
||||
{
|
||||
if (abs(cfo) > TRS_CFO_THRESH) {
|
||||
LOG_A(PHY, "CFO estimated (%d) from TRS exceeded threshold (%d). Adjusting radio CF\n", cfo, TRS_CFO_THRESH);
|
||||
ue->freq_offset += cfo;
|
||||
uint64_t dl_carrier;
|
||||
uint64_t ul_carrier;
|
||||
nr_get_carrier_frequencies(ue, &dl_carrier, &ul_carrier);
|
||||
nrue_ru_set_freq(ue, dl_carrier, ul_carrier, ue->freq_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void *UE_thread(void *arg)
|
||||
{
|
||||
//this thread should be over the processing thread to keep in real time
|
||||
|
||||
@@ -463,6 +463,7 @@ typedef struct {
|
||||
uint8_t power_control_offset; // Ratio of PDSCH EPRE to NZP CSI-RSEPRE [3GPP TS 38.214, sec 5.2.2.3.1], Value: 0->23 representing -8 to 15 dB in 1dB steps; 255: L1 is configured with ProfileSSS
|
||||
uint8_t power_control_offset_ss; // Ratio of NZP CSI-RS EPRE to SSB/PBCH block EPRE [3GPP TS 38.214, sec 5.2.2.3.1], Values: 0: -3dB; 1: 0dB; 2: 3dB; 3: 6dB; 255: L1 is configured with ProfileSSS
|
||||
uint8_t measurement_bitmap; // bit 0 RSRP, bit 1 RI, bit 2 LI, bit 3 PMI, bit 4 CQI, bit 5 i1
|
||||
uint8_t last_trs_slot; // indicates to PHY if the slot is end of TRS burst
|
||||
} fapi_nr_dl_config_csirs_pdu_rel15_t;
|
||||
|
||||
typedef enum{vrb_to_prb_mapping_non_interleaved = 0, vrb_to_prb_mapping_interleaved = 1} vrb_to_prb_mapping_t;
|
||||
|
||||
@@ -250,6 +250,7 @@ uint32_t calc_power_csirs(const uint16_t *x, const fapi_nr_dl_config_csirs_pdu_r
|
||||
|
||||
static int nr_csi_rs_channel_estimation(
|
||||
const NR_DL_FRAME_PARMS *fp,
|
||||
const int meas_bitmap,
|
||||
const fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
|
||||
const nr_csi_info_t *nr_csi_info,
|
||||
const c16_t **csi_rs_generated_signal,
|
||||
@@ -301,10 +302,16 @@ static int nr_csi_rs_channel_estimation(
|
||||
const c16_t *rx_csi_rs_signal = &csi_rs_received_signal[ant_rx][symbol_offset];
|
||||
c16_t tmp =
|
||||
c16MulConjShift(tx_csi_rs_signal[k_tx], rx_csi_rs_signal[k_rx], nr_csi_info->csi_rs_generated_signal_bits);
|
||||
if (csirs_config_pdu->csi_type != 0) {
|
||||
// This is not just the LS estimation for each (k,l), but also the sum of the different contributions
|
||||
// for the sake of optimizing the memory used.
|
||||
csi_rs_ls_estimated_channel[ant_rx][port_tx][kinit_tx].r += tmp.r;
|
||||
csi_rs_ls_estimated_channel[ant_rx][port_tx][kinit_tx].i += tmp.i;
|
||||
} else {
|
||||
// for tracking we want estimates of all sub carriers having CSI-RS
|
||||
csi_rs_ls_estimated_channel[ant_rx][port_tx][k_tx].r = tmp.r;
|
||||
csi_rs_ls_estimated_channel[ant_rx][port_tx][k_tx].i = tmp.i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,6 +369,10 @@ static int nr_csi_rs_channel_estimation(
|
||||
}
|
||||
}
|
||||
|
||||
// we need only ls estimates for tracing CSI
|
||||
if (meas_bitmap < 2)
|
||||
continue;
|
||||
|
||||
/// Power noise estimation
|
||||
AssertFatal(csirs_config_pdu->nr_of_rbs > 0, " nr_of_rbs needs to be greater than 0\n");
|
||||
uint16_t noise_real[fp->nb_antennas_rx][csi_mapping->ports][csirs_config_pdu->nr_of_rbs];
|
||||
@@ -406,9 +417,11 @@ static int nr_csi_rs_channel_estimation(
|
||||
|
||||
}
|
||||
|
||||
if (meas_bitmap > 1) {
|
||||
*noise_power /= (fp->nb_antennas_rx * csi_mapping->ports);
|
||||
*log2_maxh = log2_approx(maxh - 1);
|
||||
*log2_re = log2_approx(count - 1);
|
||||
}
|
||||
|
||||
#ifdef NR_CSIRS_DEBUG
|
||||
LOG_I(NR_PHY, "Noise power estimation based on CSI-RS: %i\n", *noise_power);
|
||||
@@ -774,6 +787,77 @@ static void nr_csi_im_power_estimation(const PHY_VARS_NR_UE *ue,
|
||||
#endif
|
||||
}
|
||||
|
||||
static double get_cfo(const double phase_diff, const int sym0, const int sym1, const NR_DL_FRAME_PARMS *fp)
|
||||
{
|
||||
const double one_fs = 1.0 / (fp->samples_per_frame * 100);
|
||||
int sample_count = 0;
|
||||
for (int s = sym0 + 1; s <= sym1; s++) {
|
||||
const int prefix = (s % (7 * (1 << fp->numerology_index))) ? fp->nb_prefix_samples : fp->nb_prefix_samples0;
|
||||
sample_count += (fp->ofdm_symbol_size + prefix);
|
||||
}
|
||||
const double delta_time = sample_count * one_fs;
|
||||
const double cfo = phase_diff / (2 * M_PI * delta_time);
|
||||
return cfo;
|
||||
}
|
||||
|
||||
static void nr_ue_trs_processing(PHY_VARS_NR_UE *ue,
|
||||
const c16_t res0_est[][1][ue->frame_parms.ofdm_symbol_size],
|
||||
const c16_t res1_est[][1][ue->frame_parms.ofdm_symbol_size],
|
||||
const c16_t freq_interp_est[][1][ue->frame_parms.ofdm_symbol_size],
|
||||
const fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
|
||||
const csi_mapping_parms_t *csi_mapping,
|
||||
const int sym0,
|
||||
const int sym1,
|
||||
int *cfo,
|
||||
int *time_offset)
|
||||
{
|
||||
AssertFatal((sym0 > -1) && (sym1 > -1), "Invalid symbol index for TRS estimation\n");
|
||||
const NR_DL_FRAME_PARMS *fp = &ue->frame_parms;
|
||||
for (int ant_rx = 0; ant_rx < fp->nb_antennas_rx; ant_rx++) {
|
||||
// CFO estimation
|
||||
double phase_diff = 0.0;
|
||||
int count = 0;
|
||||
for (int rb = csirs_config_pdu->start_rb; rb < (csirs_config_pdu->start_rb + csirs_config_pdu->nr_of_rbs); rb++) {
|
||||
// for freq density 0.5 checks if even or odd RB
|
||||
if (csirs_config_pdu->freq_density <= 1 && csirs_config_pdu->freq_density != (rb % 2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int cdm_id = 0; cdm_id < csi_mapping->size; cdm_id++) {
|
||||
// loop over frequency resource elements within a group
|
||||
for (int kp = 0; kp <= csi_mapping->kprime; kp++) {
|
||||
uint16_t kinit = rb * NR_NB_SC_PER_RB;
|
||||
uint16_t k = kinit + csi_mapping->koverline[cdm_id] + kp;
|
||||
|
||||
// loop over time resource elements within a group
|
||||
for (int lp = 0; lp <= csi_mapping->lprime; lp++) {
|
||||
const c16_t *res0 = res0_est[ant_rx][0];
|
||||
const c16_t *res1 = res1_est[ant_rx][0];
|
||||
double tmp_phase = atan2((double)res1[k].i, (double)res1[k].r) - atan2((double)res0[k].i, (double)res0[k].r);
|
||||
// wrap around phase
|
||||
tmp_phase = (tmp_phase > M_PI) ? tmp_phase - 2 * M_PI : tmp_phase;
|
||||
tmp_phase = (tmp_phase < -M_PI) ? tmp_phase + 2 * M_PI : tmp_phase;
|
||||
phase_diff += tmp_phase;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
phase_diff /= count;
|
||||
*cfo = (int)get_cfo(phase_diff, sym0, sym1, fp);
|
||||
|
||||
if (time_offset) {
|
||||
// Time offset estimation
|
||||
__attribute__((aligned(32))) c16_t time_est[fp->ofdm_symbol_size];
|
||||
delay_t delay = {0};
|
||||
nr_est_delay(fp->ofdm_symbol_size, freq_interp_est[ant_rx][0], time_est, &delay);
|
||||
*time_offset = delay.delay_max_pos;
|
||||
}
|
||||
// estimate only for first antenna port
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nr_ue_csi_im_procedures(PHY_VARS_NR_UE *ue,
|
||||
const c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP],
|
||||
const fapi_nr_dl_config_csiim_pdu_rel15_t *csiim_config_pdu)
|
||||
@@ -796,7 +880,10 @@ void nr_ue_csi_im_procedures(PHY_VARS_NR_UE *ue,
|
||||
void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
const UE_nr_rxtx_proc_t *proc,
|
||||
const c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP],
|
||||
fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu)
|
||||
fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
|
||||
c16_t trs_estimates[][1][ue->frame_parms.ofdm_symbol_size],
|
||||
const int res_idx,
|
||||
const int trs_sym0)
|
||||
{
|
||||
|
||||
#ifdef NR_CSIRS_DEBUG
|
||||
@@ -816,11 +903,6 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
LOG_I(NR_PHY, "csirs_config_pdu->power_control_offset_ss = %i\n", csirs_config_pdu->power_control_offset_ss);
|
||||
#endif
|
||||
|
||||
if(csirs_config_pdu->csi_type == 0) {
|
||||
LOG_E(NR_PHY, "Handling of CSI-RS for tracking not handled yet at PHY\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(csirs_config_pdu->csi_type == 2) {
|
||||
LOG_E(NR_PHY, "Handling of ZP CSI-RS not handled yet at PHY\n");
|
||||
return;
|
||||
@@ -870,25 +952,19 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
&rsrp_dBm,
|
||||
rxdataF);
|
||||
|
||||
|
||||
if (csirs_config_pdu->measurement_bitmap == 0) {
|
||||
LOG_D(NR_PHY, "No CSI-RS measurements configured\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t noise_power = 0;
|
||||
int16_t log2_re = 0;
|
||||
int16_t log2_maxh = 0;
|
||||
// if we need to measure only RSRP no need to do channel estimation
|
||||
if (csirs_config_pdu->measurement_bitmap > 1)
|
||||
const bool use_trs_buff = (csirs_config_pdu->csi_type == 0 && res_idx == 0);
|
||||
nr_csi_rs_channel_estimation(frame_parms,
|
||||
csirs_config_pdu->measurement_bitmap,
|
||||
csirs_config_pdu,
|
||||
csi_info,
|
||||
(const c16_t **)csi_info->csi_rs_generated_signal,
|
||||
csi_rs_received_signal,
|
||||
&mapping_parms,
|
||||
CDM_group_size,
|
||||
csi_rs_ls_estimated_channel,
|
||||
(use_trs_buff) ? trs_estimates : csi_rs_ls_estimated_channel,
|
||||
csi_rs_estimated_channel_freq,
|
||||
&log2_re,
|
||||
&log2_maxh,
|
||||
@@ -926,9 +1002,34 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
nr_csi_rs_cqi_estimation(precoded_sinr_dB, &cqi);
|
||||
}
|
||||
|
||||
int trs_cfo = 0;
|
||||
const bool do_trs_est = (csirs_config_pdu->csi_type == 0) && (res_idx == 1);
|
||||
if (do_trs_est) {
|
||||
start_meas_nr_ue_phy(ue, TRS_PROCESSING);
|
||||
nr_ue_trs_processing(ue,
|
||||
trs_estimates,
|
||||
csi_rs_ls_estimated_channel,
|
||||
csi_rs_estimated_channel_freq,
|
||||
csirs_config_pdu,
|
||||
&mapping_parms,
|
||||
trs_sym0,
|
||||
csirs_config_pdu->symb_l0,
|
||||
&trs_cfo,
|
||||
NULL); // Time offset not estimated because it is corrected using PBCH DMRS
|
||||
stop_meas_nr_ue_phy(ue, TRS_PROCESSING);
|
||||
}
|
||||
|
||||
switch (csirs_config_pdu->measurement_bitmap) {
|
||||
case 0:
|
||||
if (do_trs_est)
|
||||
LOG_I(NR_PHY,
|
||||
"%d.%d TRS estimated CFO: %d Hz\n",
|
||||
proc->frame_rx,
|
||||
proc->nr_slot_rx,
|
||||
trs_cfo);
|
||||
break;
|
||||
case 1:
|
||||
LOG_I(NR_PHY, "[UE %d] RSRP = %i dBm\n", ue->Mod_id, rsrp_dBm);
|
||||
LOG_I(NR_PHY, "%d.%d [UE %d] RSRP = %i dBm\n", proc->frame_rx, proc->nr_slot_rx, ue->Mod_id, rsrp_dBm);
|
||||
break;
|
||||
case 26 :
|
||||
LOG_I(NR_PHY, "RI = %i i1 = %i.%i.%i, i2 = %i, SINR = %i dB, CQI = %i\n",
|
||||
@@ -942,6 +1043,10 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
AssertFatal(false, "Not supported measurement configuration\n");
|
||||
}
|
||||
|
||||
if (!ue->cont_fo_comp && do_trs_est && csirs_config_pdu->last_trs_slot) {
|
||||
trs_freq_correction(ue, trs_cfo);
|
||||
}
|
||||
|
||||
// Send CSI measurements to MAC
|
||||
if (!ue->if_inst || !ue->if_inst->dl_indication)
|
||||
return;
|
||||
|
||||
@@ -8,13 +8,6 @@
|
||||
#ifndef __PHY_DEFS_NR_UE__H__
|
||||
#define __PHY_DEFS_NR_UE__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <atomic>
|
||||
#ifndef _Atomic
|
||||
#define _Atomic(X) std::atomic< X >
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "defs_nr_common.h"
|
||||
#include "CODING/nrPolar_tools/nr_polar_pbch_defs.h"
|
||||
#include "PHY/defs_nr_sl_UE.h"
|
||||
@@ -25,6 +18,16 @@
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdatomic.h>
|
||||
#else
|
||||
#include <atomic>
|
||||
#ifndef _Atomic
|
||||
#define _Atomic(X) std::atomic<X>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "common_lib.h"
|
||||
#include "fapi_nr_ue_interface.h"
|
||||
#include "assertions.h"
|
||||
@@ -57,6 +60,13 @@
|
||||
// (0 + 0 * 20) % 512 = 0
|
||||
#define NUM_PROCESS_SLOT_TX_BARRIERS 512
|
||||
|
||||
// CSI for tracking can have up to 2 resources per slot
|
||||
#define MAX_CSI_RES_SLOT 2
|
||||
// Number of consequtive slots carrying TRS
|
||||
#define NUM_TRS_SLOT 2
|
||||
// Threshold to change radio frequency
|
||||
#define TRS_CFO_THRESH 500
|
||||
|
||||
#include "impl_defs_nr.h"
|
||||
#include "time_meas.h"
|
||||
#include "PHY/CODING/coding_defs.h"
|
||||
@@ -555,7 +565,8 @@ typedef struct nr_phy_data_s {
|
||||
int n_dlsch_codewords;
|
||||
// Sidelink Rx action decided by MAC
|
||||
sl_nr_rx_config_type_enum_t sl_rx_action;
|
||||
NR_UE_CSI_RS csirs_vars;
|
||||
int num_csirs;
|
||||
NR_UE_CSI_RS csirs_vars[MAX_CSI_RES_SLOT];
|
||||
NR_UE_CSI_IM csiim_vars;
|
||||
} nr_phy_data_t;
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
FN(ULSCH_INTERLEAVING_STATS),\
|
||||
FN(ULSCH_ENCODING_STATS),\
|
||||
FN(OFDM_MOD_STATS),\
|
||||
FN(PRACH_GEN_STATS)
|
||||
FN(PRACH_GEN_STATS),\
|
||||
FN(TRS_PROCESSING)
|
||||
|
||||
typedef enum {
|
||||
FOREACH_NR_PHY_CPU_MEAS(NOOP),
|
||||
|
||||
@@ -134,7 +134,12 @@ void nr_ue_csi_im_procedures(PHY_VARS_NR_UE *ue,
|
||||
void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
const UE_nr_rxtx_proc_t *proc,
|
||||
const c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP],
|
||||
fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu);
|
||||
fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
|
||||
c16_t trs_estimates[][1][ue->frame_parms.ofdm_symbol_size],
|
||||
const int res_idx,
|
||||
const int trs_sym0);
|
||||
|
||||
void trs_freq_correction(PHY_VARS_NR_UE *ue, int cfo);
|
||||
|
||||
int psbch_pscch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data);
|
||||
void phy_procedures_nrUE_SL_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_tx_t *phy_data, c16_t **txp);
|
||||
|
||||
@@ -150,8 +150,15 @@ static void nr_ue_scheduled_response_dl(NR_UE_MAC_INST_t *mac,
|
||||
phy_data->csiim_vars.active = true;
|
||||
break;
|
||||
case FAPI_NR_DL_CONFIG_TYPE_CSI_RS:
|
||||
phy_data->csirs_vars.csirs_config_pdu = pdu->csirs_config_pdu.csirs_config_rel15;
|
||||
phy_data->csirs_vars.active = true;
|
||||
AssertFatal(phy_data->num_csirs < MAX_CSI_RES_SLOT, "CSI resources per slot exceeded limit\n");
|
||||
const int c = phy_data->num_csirs;
|
||||
if (phy_data->csirs_vars[c].active) {
|
||||
AssertFatal(false, "Resource should not be active before its configured\n");
|
||||
continue;
|
||||
}
|
||||
phy_data->csirs_vars[c].csirs_config_pdu = pdu->csirs_config_pdu.csirs_config_rel15;
|
||||
phy_data->csirs_vars[c].active = true;
|
||||
phy_data->num_csirs++;
|
||||
break;
|
||||
case FAPI_NR_DL_CONFIG_TYPE_RA_DLSCH:
|
||||
case FAPI_NR_DL_CONFIG_TYPE_SI_DLSCH:
|
||||
|
||||
@@ -1178,16 +1178,32 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_
|
||||
}
|
||||
|
||||
// do procedures for CSI-RS
|
||||
if (phy_data->csirs_vars.active == 1) {
|
||||
{
|
||||
/*
|
||||
CSI-RS for tracking use only one port.
|
||||
Number of CSI-RS resources for tracking is always 2 per slot.
|
||||
Computed estimates from first resource is saved and used while estimating second resource.
|
||||
*/
|
||||
c16_t trs_estimates[ue->frame_parms.nb_antennas_rx][1][ue->frame_parms.ofdm_symbol_size];
|
||||
for (int res = 0; res < MAX_CSI_RES_SLOT; res++) {
|
||||
if (phy_data->csirs_vars[res].active == 1) {
|
||||
for (int symb = 0; symb < ue->frame_parms.symbols_per_slot; symb++) {
|
||||
if(is_csi_rs_in_symbol(phy_data->csirs_vars.csirs_config_pdu, symb)) {
|
||||
if (is_csi_rs_in_symbol(phy_data->csirs_vars[res].csirs_config_pdu, symb)) {
|
||||
if (!slot_fep_map[symb]) {
|
||||
nr_slot_fep(ue, &ue->frame_parms, proc->nr_slot_rx, symb, rxdataF, link_type_dl, 0, ue->common_vars.rxdata);
|
||||
slot_fep_map[symb] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
nr_ue_csi_rs_procedures(ue, proc, rxdataF, &phy_data->csirs_vars.csirs_config_pdu);
|
||||
nr_ue_csi_rs_procedures(ue,
|
||||
proc,
|
||||
rxdataF,
|
||||
&phy_data->csirs_vars[res].csirs_config_pdu,
|
||||
trs_estimates,
|
||||
res,
|
||||
(res == 1) ? phy_data->csirs_vars[0].csirs_config_pdu.symb_l0 : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t *llr[2];
|
||||
|
||||
@@ -68,3 +68,7 @@ void configure_nr_nfapi_vnf(eth_params_t params)
|
||||
{
|
||||
UNUSED(params);
|
||||
}
|
||||
|
||||
void trs_freq_correction(PHY_VARS_NR_UE *ue, int cfo)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1114,7 +1114,8 @@ static void schedule_ta_command(fapi_nr_dl_config_request_t *dl_config, NR_UE_MA
|
||||
|
||||
static NR_CSI_ResourceConfigId_t find_CSI_resourceconfig(NR_CSI_MeasConfig_t *csi_measconfig,
|
||||
NR_BWP_Id_t dl_bwp_id,
|
||||
NR_NZP_CSI_RS_ResourceId_t csi_id)
|
||||
NR_NZP_CSI_RS_ResourceId_t csi_id,
|
||||
bool *is_last_res)
|
||||
{
|
||||
bool found = false;
|
||||
for (int csi_list = 0; csi_list < csi_measconfig->csi_ResourceConfigToAddModList->list.count; csi_list++) {
|
||||
@@ -1139,14 +1140,14 @@ static NR_CSI_ResourceConfigId_t find_CSI_resourceconfig(NR_CSI_MeasConfig_t *cs
|
||||
AssertFatal(csi_res->nzp_CSI_RS_Resources.list.array[k], "NZP_CSI_RS_ResourceId shoulan't be NULL\n");
|
||||
if (csi_id == *csi_res->nzp_CSI_RS_Resources.list.array[k]) {
|
||||
found = true;
|
||||
*is_last_res = (k == (csi_res->nzp_CSI_RS_Resources.list.count - 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found && csi_res->trs_Info)
|
||||
// CRI-RS for Tracking (not implemented yet)
|
||||
// in this case we there is no associated CSI report
|
||||
// therefore to signal this we return a value higher than
|
||||
// maxNrofCSI-ResourceConfigurations
|
||||
/* CRI-RS for Tracking. In this case there is no associated CSI report
|
||||
* therefore to signal this we return a value higher than
|
||||
* maxNrofCSI-ResourceConfigurations. */
|
||||
return NR_maxNrofCSI_ResourceConfigurations + 1;
|
||||
else if (found)
|
||||
return csires->csi_ResourceConfigId;
|
||||
@@ -1216,7 +1217,8 @@ static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int sl
|
||||
csi_period_offset(NULL, nzpcsi->periodicityAndOffset, &period, &offset);
|
||||
if((frame * mac->frame_structure.numb_slots_frame + slot-offset) % period != 0)
|
||||
continue;
|
||||
NR_CSI_ResourceConfigId_t csi_res_id = find_CSI_resourceconfig(csi_measconfig, dl_bwp_id, nzpcsi->nzp_CSI_RS_ResourceId);
|
||||
bool is_last_res = false;
|
||||
NR_CSI_ResourceConfigId_t csi_res_id = find_CSI_resourceconfig(csi_measconfig, dl_bwp_id, nzpcsi->nzp_CSI_RS_ResourceId, &is_last_res);
|
||||
// do not schedule reseption of this CSI-RS if not associated with current BWP
|
||||
if(csi_res_id < 0)
|
||||
continue;
|
||||
@@ -1226,9 +1228,12 @@ static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int sl
|
||||
csirs_config_pdu->subcarrier_spacing = mu;
|
||||
csirs_config_pdu->cyclic_prefix = current_DL_BWP->cyclicprefix ? *current_DL_BWP->cyclicprefix : 0;
|
||||
|
||||
if (csi_res_id > NR_maxNrofCSI_ResourceConfigurations)
|
||||
if (csi_res_id > NR_maxNrofCSI_ResourceConfigurations) {
|
||||
/* According to 38.214 5.1.6.1.1, the number of resources indicate if one
|
||||
* or two consequtive slots for TRS is used. We indicate to phy the last slot. */
|
||||
csirs_config_pdu->last_trs_slot = is_last_res;
|
||||
csirs_config_pdu->csi_type = 0; // TRS
|
||||
else
|
||||
} else
|
||||
csirs_config_pdu->csi_type = 1; // NZP-CSI-RS
|
||||
|
||||
csirs_config_pdu->scramb_id = nzpcsi->scramblingID;
|
||||
|
||||
@@ -44,11 +44,13 @@
|
||||
#define ZMQ_SECTION "zmq"
|
||||
#define ZMQ_TX_CHANNELS "tx_channels"
|
||||
#define ZMQ_RX_CHANNELS "rx_channels"
|
||||
#define ZMQ_CTRL_CHANNELS "ctrl_channels"
|
||||
|
||||
#define ZMQ_PARAMS_DESC \
|
||||
{ \
|
||||
STRINGLISTPARAM(ZMQ_TX_CHANNELS, "list of zmq addresses represeting tx channels_\n", PARAMFLAG_MANDATORY, nullptr, nullptr), \
|
||||
STRINGLISTPARAM(ZMQ_RX_CHANNELS, "list of zmq addresses represeting rx channels_\n", PARAMFLAG_MANDATORY, nullptr, nullptr), \
|
||||
STRINGLISTPARAM(ZMQ_CTRL_CHANNELS, "list of zmq addresses represeting control channels_\n", PARAMFLAG_MANDATORY, nullptr, nullptr), \
|
||||
};
|
||||
|
||||
const size_t sample_size = sizeof(cf_t);
|
||||
@@ -56,6 +58,8 @@ const size_t rx_buffer_size = sample_size * 300000;
|
||||
|
||||
typedef struct {
|
||||
void *context;
|
||||
void *ctrl_context;
|
||||
void *ctrl_socket;
|
||||
zmq_tx_stream tx_stream;
|
||||
zmq_rx_stream rx_stream;
|
||||
std::thread poll_thread;
|
||||
@@ -224,6 +228,10 @@ static void zmq_end(openair0_device_t *device)
|
||||
|
||||
if (s->context)
|
||||
zmq_ctx_destroy(s->context);
|
||||
if (s->ctrl_socket)
|
||||
zmq_close(s->ctrl_socket);
|
||||
if (s->ctrl_context)
|
||||
zmq_ctx_destroy(s->ctrl_context);
|
||||
delete s;
|
||||
}
|
||||
}
|
||||
@@ -255,8 +263,18 @@ static int zmq_stop(openair0_device_t *device)
|
||||
|
||||
static int zmq_set_freq(openair0_device_t *device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
return 0;
|
||||
/* Send raw 8-byte little-endian double - matches struct.unpack('d') in Python */
|
||||
double freq = openair0_cfg[0].rx_freq[0];
|
||||
zmq_state_t *s = static_cast<zmq_state_t *>(device->priv);
|
||||
int rc = zmq_send(s->ctrl_socket, &freq, sizeof(double), 0);
|
||||
if (rc != sizeof(double)) {
|
||||
LOG_E(HW, "[ZMQ] send failed: %s\n", zmq_strerror(zmq_errno()));
|
||||
} else {
|
||||
LOG_I(HW, "[ZMQ] sent rx freq = %.4f Hz\n", freq);
|
||||
}
|
||||
return (rc == sizeof(double)) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int zmq_set_gains(openair0_device_t *device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
return 0;
|
||||
@@ -325,6 +343,21 @@ extern "C" __attribute__((__visibility__("default"))) int device_init(openair0_d
|
||||
zmq_state->rx_stream.tx_stream_ = &zmq_state->tx_stream;
|
||||
}
|
||||
|
||||
// Setup control channel
|
||||
int num_ctrl_channels = gpd(param_desc, sizeofArray(param_desc), ZMQ_CTRL_CHANNELS)->numelt;
|
||||
if (num_ctrl_channels > 0) {
|
||||
AssertFatal(num_ctrl_channels == 1, "Only one ZMQ control channel is supported at the moment.\n");
|
||||
char **ctrl_channels = gpd(param_desc, sizeofArray(param_desc), ZMQ_CTRL_CHANNELS)->strlistptr;
|
||||
zmq_state->ctrl_context = zmq_ctx_new();
|
||||
zmq_state->ctrl_socket = zmq_socket(zmq_state->ctrl_context, ZMQ_PUSH);
|
||||
AssertFatal(zmq_state->ctrl_socket != NULL, "zmq_socket(ZMQ_PUSH) for control channel failed");
|
||||
int linger = 0;
|
||||
zmq_setsockopt(zmq_state->ctrl_socket, ZMQ_LINGER, &linger, sizeof(linger));
|
||||
AssertFatal(zmq_connect(zmq_state->ctrl_socket, ctrl_channels[0]) == 0,
|
||||
"zmq_bind for control channel failed on %s",
|
||||
ctrl_channels[0]);
|
||||
}
|
||||
|
||||
device->trx_start_func = zmq_start;
|
||||
device->trx_get_stats_func = zmq_get_stats;
|
||||
device->trx_reset_stats_func = zmq_reset_stats;
|
||||
|
||||
Reference in New Issue
Block a user