mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
fix(oru): Fix timestamp generation in O-RU
Instead of guessing what is the absolute timestamp of each symbol returned from fronthaul, add absolute hyper frame from GPS Epoch to the O-RU. The O-RU can calculate the absolute sample index from hyper frame reliably regardless of the frame/slot/symbol ordering. Add config file for 1x1 O-DU that matches the edited O-RU config. Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
This commit is contained in:
@@ -344,33 +344,28 @@ void *oru_north_read_thread(void *arg)
|
||||
txDataF_ptr[aatx] = txDataF[aatx];
|
||||
}
|
||||
uint32_t start_frame, start_slot;
|
||||
uint64_t start_hyper_frame;
|
||||
struct timespec utc_anchor_point;
|
||||
oru_fh_get_utc_anchor_point(oru->fronthaul, &start_frame, &start_slot, &utc_anchor_point);
|
||||
oru_fh_get_utc_anchor_point(oru->fronthaul, &start_hyper_frame, &start_frame, &start_slot, &utc_anchor_point);
|
||||
AssertFatal(ru->rfdevice.get_timestamp != NULL, "rfdevice has no capability to translate UTC timestamp to sample index\n");
|
||||
int64_t start_timestamp = ru->rfdevice.get_timestamp(&ru->rfdevice, &utc_anchor_point);
|
||||
// subtract the start_frame and start_slot from the timestamp simplify calculation below.
|
||||
start_timestamp -= (start_frame * fp->samples_per_frame + get_samples_slot_timestamp(fp, start_slot));
|
||||
// Now start_timestamp points to the start sample of the frame 0 slot 0 symbol 0 of hyperframe 0
|
||||
uint64_t hyper_frame_number = 0;
|
||||
uint32_t last_frame = 0;
|
||||
LOG_A(PHY, "DL thread started: start_timestamp %ld, start_frame %d, start_slot %d\n", start_timestamp, start_frame, start_slot);
|
||||
|
||||
while (!oai_exit) {
|
||||
int frame = -1, slot = -1, symbol = -1;
|
||||
int ret = oru_fh_tx_read_symbol(oru->fronthaul, (uint32_t **)txDataF_ptr, ru->nb_tx, &frame, &slot, &symbol);
|
||||
uint64_t hyper_frame;
|
||||
int ret = oru_fh_tx_read_symbol(oru->fronthaul, (uint32_t **)txDataF_ptr, ru->nb_tx, &hyper_frame, &frame, &slot, &symbol);
|
||||
if (ret != 0) {
|
||||
LOG_E(PHY, "[RU_thread] read data error: frame %d, slot %d, symbol %d\n", frame, slot, symbol);
|
||||
continue;
|
||||
}
|
||||
if (last_frame + 1 != frame) {
|
||||
if (frame == 0) {
|
||||
hyper_frame_number++;
|
||||
} else if (last_frame != frame ) {
|
||||
LOG_E(PHY, "Received frames out of order, last_frame %d, frame %d\n", last_frame, frame);
|
||||
}
|
||||
if (start_hyper_frame > hyper_frame) {
|
||||
continue;
|
||||
}
|
||||
last_frame = frame;
|
||||
uint64_t num_frames = hyper_frame_number * 1024 + frame;
|
||||
uint64_t num_frames = (hyper_frame - start_hyper_frame) * 1024 + frame;
|
||||
int64_t timestamp = start_timestamp + num_frames * fp->samples_per_frame + get_samples_slot_timestamp(fp, slot)
|
||||
+ get_samples_symbol_timestamp(fp, slot, symbol);
|
||||
if (timestamp < 0) {
|
||||
|
||||
@@ -223,12 +223,12 @@ void oru_fh_cleanup(void *handle)
|
||||
free(fh);
|
||||
}
|
||||
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol)
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol)
|
||||
{
|
||||
if (!handle)
|
||||
return -1;
|
||||
oru_fh_t *fh = (oru_fh_t *)handle;
|
||||
read_dl_iq(fh->packet_processor, txdataF, nb_tx, frame, slot, symbol);
|
||||
read_dl_iq(fh->packet_processor, txdataF, nb_tx, hyper_frame, frame, slot, symbol);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ int oru_fh_get_ready_jobs(void *handle)
|
||||
return get_ready_job_count(fh->packet_processor);
|
||||
}
|
||||
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint32_t *frame, uint32_t *slot, struct timespec *ts)
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint64_t *hyper_frame, uint32_t *frame, uint32_t *slot, struct timespec *ts)
|
||||
{
|
||||
if (!handle || !frame || !slot || !ts)
|
||||
return -1;
|
||||
@@ -249,6 +249,7 @@ int oru_fh_get_utc_anchor_point(void *handle, uint32_t *frame, uint32_t *slot, s
|
||||
absolute_gps_symbol -= absolute_gps_symbol % NR_SYMBOLS_PER_SLOT; // Round down to start of current slot
|
||||
uint64_t absolute_slot = absolute_gps_symbol / NR_SYMBOLS_PER_SLOT;
|
||||
uint32_t slots_per_frame = 10 << fh->cfg.numerology;
|
||||
*hyper_frame = (absolute_slot / slots_per_frame) / 1024;
|
||||
*frame = (absolute_slot / slots_per_frame) % 1024;
|
||||
*slot = absolute_slot % slots_per_frame;
|
||||
|
||||
|
||||
@@ -74,23 +74,25 @@ int oru_fh_get_ready_jobs(void *handle);
|
||||
* @param handle Pointer to the fronthaul handle.
|
||||
* @param txdataF Array of pointers to buffers to store the received frequency-domain IQ samples (per TX antenna).
|
||||
* @param nb_tx Number of TX antennas.
|
||||
* @param hyper_frame Absolute GPS hyper-frame number
|
||||
* @param frame Pointer to store the frame number of the read symbol.
|
||||
* @param slot Pointer to store the slot number of the read symbol.
|
||||
* @param symbol Pointer to store the symbol number.
|
||||
* @return 0 on success or -1 on failure
|
||||
*/
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol);
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol);
|
||||
|
||||
/**
|
||||
* @brief Get the UTC anchor point mapping between 5G time and system time.
|
||||
*
|
||||
* @param handle Pointer to the fronthaul handle.
|
||||
* @param frame Pointer to store the reference frame number.
|
||||
* @param hyper_frame Pointer to store the reference hyperframe number (1024 frames each)
|
||||
* @param slot Pointer to store the reference slot number.
|
||||
* @param ts Pointer to a timespec structure to store the corresponding system time.
|
||||
* @return 0 on success, negative on error.
|
||||
*/
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint32_t* frame, uint32_t* slot, struct timespec *ts);
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint64_t *hyper_frame, uint32_t* frame, uint32_t* slot, struct timespec *ts);
|
||||
|
||||
/**
|
||||
* @brief Send PRACH symbol data (U-Plane) over the Fronthaul interface.
|
||||
|
||||
@@ -739,7 +739,7 @@ static void unpack_iq(c16_t *txdataF, void *iqdata, int start_prb, int num_prb)
|
||||
}
|
||||
}
|
||||
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol)
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol)
|
||||
{
|
||||
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
|
||||
if (ctx == NULL)
|
||||
@@ -754,6 +754,7 @@ void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *s
|
||||
uint64_t absolute_gps_symbol = job->absolute_symbol;
|
||||
int numerology = ctx->numerology;
|
||||
int num_symbols_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << numerology) * NR_SYMBOLS_PER_SLOT;
|
||||
*hyper_frame = (absolute_gps_symbol / num_symbols_per_frame) / 1024;
|
||||
*frame = (absolute_gps_symbol / num_symbols_per_frame) % 1024;
|
||||
*slot = (absolute_gps_symbol % num_symbols_per_frame) / NR_SYMBOLS_PER_SLOT;
|
||||
*symbol = absolute_gps_symbol % NR_SYMBOLS_PER_SLOT;
|
||||
|
||||
@@ -68,7 +68,7 @@ void handle_uplane_packet(void *context, void *pkt);
|
||||
void handle_cplane_packet(void *context, void *pkt);
|
||||
void print_packet_processor_stats(void *context);
|
||||
void get_packet_processor_stats(void *context, oru_packet_processor_stats_t *out_stats);
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol);
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol);
|
||||
int get_ready_job_count(void *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <unistd.h>
|
||||
#include "common/config/config_userapi.h"
|
||||
#include <rte_eal.h>
|
||||
#include <assert.h>
|
||||
|
||||
// OAI Linkage Satisfiers
|
||||
void exit_function(const char *file, const char *function, const int line, const char *s, const int assertflag)
|
||||
@@ -84,6 +85,20 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Testing UTC Anchor Point and Hyper-frame
|
||||
printf("Testing UTC Anchor Point and Hyper-frame...\n");
|
||||
uint64_t hf;
|
||||
uint32_t f, s;
|
||||
struct timespec ts;
|
||||
if (oru_fh_get_utc_anchor_point(handle, &hf, &f, &s, &ts) < 0) {
|
||||
fprintf(stderr, "FAIL: oru_fh_get_utc_anchor_point failed\n");
|
||||
oru_fh_cleanup(handle);
|
||||
return 1;
|
||||
}
|
||||
printf("UTC Anchor Point: hf=%lu, f=%u, s=%u, ts=%ld.%09ld\n", hf, f, s, ts.tv_sec, ts.tv_nsec);
|
||||
assert(f < 1024);
|
||||
assert(s < (10 << cfg.numerology));
|
||||
|
||||
printf("Running live loop for 2 seconds...\n");
|
||||
uint32_t *txData[1];
|
||||
txData[0] = malloc(273 * 12 * sizeof(uint32_t));
|
||||
@@ -96,8 +111,9 @@ int main(int argc, char **argv)
|
||||
uint64_t target_cycles = start_cycles + (rte_get_timer_hz() / 1000);
|
||||
while (rte_get_timer_cycles() < target_cycles) {
|
||||
int f, s, sym;
|
||||
uint64_t hf;
|
||||
while (oru_fh_get_ready_jobs(handle) > 0) {
|
||||
oru_fh_tx_read_symbol(handle, txData, 1, &f, &s, &sym);
|
||||
oru_fh_tx_read_symbol(handle, txData, 1, &hf, &f, &s, &sym);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,8 +235,9 @@ void test_cplane_uplane_match()
|
||||
txdataF[0] = output_iq;
|
||||
|
||||
int frame, slot, symbol;
|
||||
uint64_t hyper_frame;
|
||||
do {
|
||||
read_dl_iq(ctx, txdataF, 1, &frame, &slot, &symbol);
|
||||
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
|
||||
} while (!(frame == (target_sym / num_symbols_per_frame) % 1024 && symbol == target_sym % 14));
|
||||
|
||||
assert(symbol == target_sym % 14);
|
||||
@@ -343,8 +344,9 @@ void test_frame_wrap_around()
|
||||
txdataF[0] = output_iq;
|
||||
|
||||
int frame, slot, symbol;
|
||||
uint64_t hyper_frame;
|
||||
do {
|
||||
read_dl_iq(ctx, txdataF, 1, &frame, &slot, &symbol);
|
||||
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
|
||||
} while (!(frame == (target_sym / num_symbols_per_frame) % 1024 && symbol == target_sym % 14));
|
||||
|
||||
assert(frame == (target_sym / num_symbols_per_frame) % 1024);
|
||||
@@ -456,9 +458,10 @@ void test_cplane_14_symbols()
|
||||
|
||||
for (int i = 0; i < 14; i++) {
|
||||
int frame, slot, symbol;
|
||||
uint64_t hyper_frame;
|
||||
uint64_t sym_i = target_sym + i;
|
||||
do {
|
||||
read_dl_iq(ctx, txdataF, 1, &frame, &slot, &symbol);
|
||||
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
|
||||
} while (!(frame == (sym_i / num_symbols_per_frame) % 1024 && symbol == sym_i % 14));
|
||||
|
||||
assert(frame == (sym_i / num_symbols_per_frame) % 1024);
|
||||
@@ -566,8 +569,9 @@ void test_other_bw_4ant_prb_offset()
|
||||
txdataF[3] = out_iq3;
|
||||
|
||||
int frame, slot, symbol;
|
||||
uint64_t hyper_frame;
|
||||
do {
|
||||
read_dl_iq(ctx, txdataF, 4, &frame, &slot, &symbol);
|
||||
read_dl_iq(ctx, txdataF, 4, &hyper_frame, &frame, &slot, &symbol);
|
||||
} while (!(frame == frameId && symbol == startSymbolId));
|
||||
|
||||
// Verify memory contents for each antenna
|
||||
@@ -1129,6 +1133,102 @@ void test_prach_generation()
|
||||
printf("PRACH generation passed!\n");
|
||||
}
|
||||
|
||||
void test_hyper_frame_calculation()
|
||||
{
|
||||
printf("Testing hyper-frame calculation...\n");
|
||||
int mu = 1; // 30kHz
|
||||
int slots_per_subframe = 1 << mu;
|
||||
void *ctx = init_packet_processor(mu, 273, 200, 400, 100, 300, 2, 2, 0, 0, 5, test_alloc_mbuf, test_send_mbuf, NULL, 1500, 0);
|
||||
assert(ctx != NULL);
|
||||
|
||||
int num_symbols_per_frame = 10 * slots_per_subframe * 14; // 280
|
||||
|
||||
// One hyper-frame has 1024 frames. So 1024 * 280 = 286720 symbols.
|
||||
// Target absolute symbol index: 3 * 286720 + 5 * 280 + 1 * 14 + 7 = 860160 + 1400 + 14 + 7 = 861581.
|
||||
uint64_t target_sym = 861581;
|
||||
uint64_t current_sym = target_sym - 7;
|
||||
handle_absolute_symbol_tick(ctx, current_sym);
|
||||
|
||||
// 1. Send C-plane packet for target_sym
|
||||
struct rte_mbuf *c_mbuf = rte_pktmbuf_alloc(mp);
|
||||
struct xran_ecpri_hdr *ecpri = (struct xran_ecpri_hdr *)rte_pktmbuf_append(c_mbuf, sizeof(struct xran_ecpri_hdr));
|
||||
ecpri->ecpri_xtc_id = xran_compose_cid(&g_eaxcid_config, 0, 0, 0, 0);
|
||||
|
||||
struct xran_cp_radioapp_section1_header *apphdr =
|
||||
(struct xran_cp_radioapp_section1_header *)rte_pktmbuf_append(c_mbuf, sizeof(struct xran_cp_radioapp_section1_header));
|
||||
memset(apphdr, 0, sizeof(*apphdr));
|
||||
apphdr->cmnhdr.field.dataDirection = XRAN_DIR_DL;
|
||||
apphdr->cmnhdr.field.payloadVer = XRAN_PAYLOAD_VER;
|
||||
|
||||
apphdr->cmnhdr.field.frameId = (target_sym / num_symbols_per_frame) % 256;
|
||||
int slot_in_frame = (target_sym % num_symbols_per_frame) / 14;
|
||||
apphdr->cmnhdr.field.subframeId = slot_in_frame / slots_per_subframe;
|
||||
apphdr->cmnhdr.field.slotId = slot_in_frame % slots_per_subframe;
|
||||
apphdr->cmnhdr.field.startSymbolId = target_sym % 14;
|
||||
apphdr->cmnhdr.sectionType = XRAN_CP_SECTIONTYPE_1;
|
||||
apphdr->cmnhdr.field.all_bits = rte_cpu_to_be_32(apphdr->cmnhdr.field.all_bits);
|
||||
|
||||
struct xran_cp_radioapp_section1 *sec =
|
||||
(struct xran_cp_radioapp_section1 *)rte_pktmbuf_append(c_mbuf, sizeof(struct xran_cp_radioapp_section1));
|
||||
memset(sec, 0, sizeof(*sec));
|
||||
sec->hdr.u.s1.numSymbol = 1;
|
||||
sec->hdr.u1.common.numPrbc = 1;
|
||||
*((uint64_t *)sec) = rte_be_to_cpu_64(*((uint64_t *)sec));
|
||||
|
||||
handle_cplane_packet(ctx, c_mbuf);
|
||||
|
||||
// 2. Send U-plane packet for target_sym
|
||||
current_sym += 3;
|
||||
handle_absolute_symbol_tick(ctx, current_sym);
|
||||
|
||||
struct rte_mbuf *u_mbuf = rte_pktmbuf_alloc(mp);
|
||||
struct xran_ecpri_hdr *u_ecpri = (struct xran_ecpri_hdr *)rte_pktmbuf_append(u_mbuf, sizeof(struct xran_ecpri_hdr));
|
||||
u_ecpri->ecpri_xtc_id = xran_compose_cid(&g_eaxcid_config, 0, 0, 0, 0);
|
||||
|
||||
struct radio_app_common_hdr *u_app =
|
||||
(struct radio_app_common_hdr *)rte_pktmbuf_append(u_mbuf, sizeof(struct radio_app_common_hdr));
|
||||
u_app->frame_id = (target_sym / num_symbols_per_frame) % 256;
|
||||
u_app->sf_slot_sym.subframe_id = slot_in_frame / slots_per_subframe;
|
||||
u_app->sf_slot_sym.slot_id = slot_in_frame % slots_per_subframe;
|
||||
u_app->sf_slot_sym.symb_id = target_sym % 14;
|
||||
u_app->sf_slot_sym.value = rte_cpu_to_be_16(u_app->sf_slot_sym.value);
|
||||
|
||||
struct data_section_hdr *u_data = (struct data_section_hdr *)rte_pktmbuf_append(u_mbuf, sizeof(struct data_section_hdr));
|
||||
u_data->fields.num_prbu = 1;
|
||||
u_data->fields.start_prbu = 0;
|
||||
u_data->fields.sect_id = 0;
|
||||
u_data->fields.all_bits = rte_cpu_to_be_32(u_data->fields.all_bits);
|
||||
|
||||
// IQ Data
|
||||
uint16_t *iq = (uint16_t *)rte_pktmbuf_append(u_mbuf, 1 * 12 * 4);
|
||||
assert(iq != NULL);
|
||||
iq[0] = 0xAAAA;
|
||||
|
||||
handle_uplane_packet(ctx, u_mbuf);
|
||||
|
||||
// 3. Advance to trigger window expiry and job completion
|
||||
current_sym += 10;
|
||||
handle_absolute_symbol_tick(ctx, current_sym);
|
||||
|
||||
uint32_t *txdataF[1] = {0};
|
||||
uint32_t output_iq[273 * 12] = {0};
|
||||
txdataF[0] = output_iq;
|
||||
|
||||
int frame, slot, symbol;
|
||||
uint64_t hyper_frame = 0xFFFFFFFF;
|
||||
do {
|
||||
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
|
||||
} while (!(frame == (target_sym / num_symbols_per_frame) % 1024 && symbol == target_sym % 14));
|
||||
|
||||
assert(hyper_frame == 3);
|
||||
assert(frame == 5);
|
||||
assert(slot == 1);
|
||||
assert(symbol == 7);
|
||||
|
||||
cleanup_packet_processor(ctx);
|
||||
printf("Hyper-frame calculation test passed!\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
setup_dpdk(argc, argv);
|
||||
@@ -1155,6 +1255,8 @@ int main(int argc, char **argv)
|
||||
usleep(10000);
|
||||
test_prach_generation();
|
||||
usleep(10000);
|
||||
test_hyper_frame_calculation();
|
||||
usleep(10000);
|
||||
|
||||
printf("All tests passed!\n");
|
||||
return 0;
|
||||
|
||||
@@ -205,8 +205,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Drain ready jobs to prevent ring overflow
|
||||
int f, sl, sy;
|
||||
uint64_t hf;
|
||||
while (get_ready_job_count(ctx) > 0) {
|
||||
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &f, &sl, &sy);
|
||||
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &hf, &f, &sl, &sy);
|
||||
}
|
||||
}
|
||||
last_tick_sym = current_sym;
|
||||
@@ -238,8 +239,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Drain ready jobs to prevent ring overflow during flush
|
||||
int f, sl, sy;
|
||||
uint64_t hf;
|
||||
while (get_ready_job_count(ctx) > 0) {
|
||||
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &f, &sl, &sy);
|
||||
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &hf, &f, &sl, &sy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user