mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
Merge remote-tracking branch 'bpodrygajlo/fix-gps-offset' into integration_2026_w25
fronthaul library fixes (#150) three fixes: - add missing spdx license identifier for some files - fix utc-to-gps offset - make fh_timer robust in case of PTP Sync Reviewed-by: Teodora Vladić <teodora.vladic@openairinterface.org> Reviewed-By: Merkebu Girmay <merkebu.girmay@openairinterface.org>
This commit is contained in:
@@ -18,7 +18,7 @@ static uint64_t get_gps_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX) * NS_PER_SEC + ts.tv_nsec;
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX + GPS_LEAP_SECONDS) * NS_PER_SEC + ts.tv_nsec;
|
||||
}
|
||||
|
||||
void fh_timer_tick(fh_timer_t *timer)
|
||||
@@ -31,8 +31,17 @@ void fh_timer_tick(fh_timer_t *timer)
|
||||
typedef __int128_t int128;
|
||||
uint64_t total_syms_per_ms = 14 << timer->numerology;
|
||||
|
||||
// Boundary(ies) crossed
|
||||
while (now >= timer->target_gps_ns && timer->running) {
|
||||
// 1. Calculate how many symbols *should* have elapsed up to 'now'
|
||||
// This catches up if the clock jumped forward, or processes at least 1 symbol.
|
||||
uint64_t expected_s_abs = (uint64_t)((int128)now * total_syms_per_ms / NS_PER_MS);
|
||||
|
||||
// PTP sync backwards adjustment - don't run callbacks until we catch up to the previously expected symbol index
|
||||
if (expected_s_abs < timer->next_s_abs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Drive the loop using the absolute symbol counter, not the volatile 'now' time
|
||||
while (timer->next_s_abs <= expected_s_abs && timer->running) {
|
||||
uint64_t s_abs = timer->next_s_abs;
|
||||
rte_atomic64_set(&timer->s_abs, s_abs);
|
||||
|
||||
@@ -43,8 +52,10 @@ void fh_timer_tick(fh_timer_t *timer)
|
||||
|
||||
// Prepare for next symbol
|
||||
timer->next_s_abs++;
|
||||
timer->target_gps_ns = (uint64_t)((int128)timer->next_s_abs * NS_PER_MS / total_syms_per_ms);
|
||||
}
|
||||
|
||||
// 3. Recalculate the next target timestamp based purely on the new symbol index
|
||||
timer->target_gps_ns = (uint64_t)((int128)timer->next_s_abs * NS_PER_MS / total_syms_per_ms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ typedef void (*fh_timer_cb)(uint64_t s_abs, void *user_data);
|
||||
|
||||
#define MAX_FH_TIMER_CBS 8
|
||||
#define GPS_EPOCH_OFFSET_UNIX 315964800ULL
|
||||
#define GPS_LEAP_SECONDS 18
|
||||
|
||||
typedef struct {
|
||||
fh_timer_cb fn;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
|
||||
set(DPDK_TEST_ARGS "--no-huge" "--iova-mode=va" "--no-pci")
|
||||
|
||||
add_executable(test_fh_timer test_fh_timer.c)
|
||||
@@ -7,6 +9,13 @@ add_dependencies(tests test_fh_timer)
|
||||
add_test(NAME test_fh_timer COMMAND test_fh_timer ${DPDK_TEST_ARGS} "--file-prefix=test_fh_timer")
|
||||
set_property(TEST test_fh_timer PROPERTY LABELS fronthaul)
|
||||
|
||||
add_executable(test_fh_timer_jumps test_fh_timer_jumps.c)
|
||||
target_link_libraries(test_fh_timer_jumps PRIVATE fh_timer ${dpdk_LIBRARIES} "-Wl,--wrap=clock_gettime")
|
||||
target_include_directories(test_fh_timer_jumps PRIVATE ${dpdk_INCLUDE_DIRS})
|
||||
add_dependencies(tests test_fh_timer_jumps)
|
||||
add_test(NAME test_fh_timer_jumps COMMAND test_fh_timer_jumps ${DPDK_TEST_ARGS} "--file-prefix=test_fh_timer_jumps")
|
||||
set_property(TEST test_fh_timer_jumps PROPERTY LABELS fronthaul)
|
||||
|
||||
add_executable(test_fh_timer_drift test_fh_timer_drift.c)
|
||||
target_link_libraries(test_fh_timer_drift PRIVATE fh_timer ${dpdk_LIBRARIES} m)
|
||||
target_include_directories(test_fh_timer_drift PRIVATE ${dpdk_INCLUDE_DIRS})
|
||||
|
||||
@@ -25,7 +25,7 @@ static uint64_t get_gps_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX) * NS_PER_SEC + ts.tv_nsec;
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX + GPS_LEAP_SECONDS) * NS_PER_SEC + ts.tv_nsec;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -19,7 +19,7 @@ static uint64_t get_gps_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX) * NS_PER_SEC + ts.tv_nsec;
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX + GPS_LEAP_SECONDS) * NS_PER_SEC + ts.tv_nsec;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -20,7 +20,7 @@ static uint64_t get_gps_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX) * NS_PER_SEC + ts.tv_nsec;
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX + GPS_LEAP_SECONDS) * NS_PER_SEC + ts.tv_nsec;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
||||
179
fronthaul/core/tests/test_fh_timer_jumps.c
Normal file
179
fronthaul/core/tests/test_fh_timer_jumps.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "fh_timer.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define GPS_EPOCH_OFFSET_UNIX 315964800ULL
|
||||
#define NS_PER_SEC 1000000000ULL
|
||||
|
||||
// Mock clock state
|
||||
static struct timespec mock_time = {0, 0};
|
||||
|
||||
// Mock implementation of clock_gettime
|
||||
int __wrap_clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
{
|
||||
if (clk_id == CLOCK_REALTIME) {
|
||||
*tp = mock_time;
|
||||
return 0;
|
||||
}
|
||||
extern int __real_clock_gettime(clockid_t clk_id, struct timespec * tp);
|
||||
return __real_clock_gettime(clk_id, tp);
|
||||
}
|
||||
|
||||
static struct timespec gps_ns_to_timespec(uint64_t gps_ns)
|
||||
{
|
||||
struct timespec ts;
|
||||
uint64_t unix_ns = gps_ns + (GPS_EPOCH_OFFSET_UNIX - GPS_LEAP_SECONDS) * NS_PER_SEC;
|
||||
ts.tv_sec = unix_ns / NS_PER_SEC;
|
||||
ts.tv_nsec = unix_ns % NS_PER_SEC;
|
||||
return ts;
|
||||
}
|
||||
|
||||
#define MAX_CALLBACKS 100
|
||||
typedef struct {
|
||||
uint64_t symbols[MAX_CALLBACKS];
|
||||
int count;
|
||||
} cb_tracker_t;
|
||||
|
||||
static void test_cb(uint64_t s_abs, void *user_data)
|
||||
{
|
||||
cb_tracker_t *tracker = (cb_tracker_t *)user_data;
|
||||
if (tracker->count < MAX_CALLBACKS) {
|
||||
tracker->symbols[tracker->count++] = s_abs;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// 1. Initialize timer under mock clock
|
||||
// Set mock time to UNIX 1718000000 (June 2024)
|
||||
mock_time.tv_sec = 1718000000;
|
||||
mock_time.tv_nsec = 0;
|
||||
|
||||
fh_timer_t timer;
|
||||
int numerology = 1; // 30kHz
|
||||
if (fh_timer_init(&timer, numerology) < 0) {
|
||||
fprintf(stderr, "Timer init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
cb_tracker_t tracker = {0};
|
||||
if (fh_timer_register_cb(&timer, test_cb, &tracker) < 0) {
|
||||
fprintf(stderr, "Callback registration failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Verify initial state
|
||||
// Expected target is 35714 ns past the initial time (Unix 1718000000s)
|
||||
printf("Initial symbol abs: %lu, expected next_s_abs: %lu\n", rte_atomic64_read(&timer.s_abs), timer.next_s_abs);
|
||||
assert(timer.next_s_abs == rte_atomic64_read(&timer.s_abs) + 1);
|
||||
|
||||
// --- Test Case 1: No tick if target not reached ---
|
||||
fh_timer_tick(&timer);
|
||||
if (tracker.count != 0) {
|
||||
fprintf(stderr, "FAIL: callback triggered before target time\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Test Case 1 passed: No premature callback.\n");
|
||||
|
||||
// --- Test Case 2: Normal Tick when target is reached ---
|
||||
// Advance mock time by 36000 ns (slightly more than 35714 ns symbol duration)
|
||||
mock_time.tv_nsec = 36000;
|
||||
fh_timer_tick(&timer);
|
||||
if (tracker.count != 1) {
|
||||
fprintf(stderr, "FAIL: callback not triggered when target reached (count=%d)\n", tracker.count);
|
||||
return 1;
|
||||
}
|
||||
if (tracker.symbols[0] != rte_atomic64_read(&timer.s_abs)) {
|
||||
fprintf(stderr,
|
||||
"FAIL: callback symbol %lu does not match current timer symbol %lu\n",
|
||||
tracker.symbols[0],
|
||||
rte_atomic64_read(&timer.s_abs));
|
||||
return 1;
|
||||
}
|
||||
printf("Test Case 2 passed: Normal tick called successfully.\n");
|
||||
|
||||
// --- Test Case 3: Clock Jump Forward (Catching Up) ---
|
||||
// Reset tracker
|
||||
tracker.count = 0;
|
||||
// Jump clock forward by 250,000 ns (roughly 7 symbol durations)
|
||||
mock_time.tv_nsec = 260000;
|
||||
fh_timer_tick(&timer);
|
||||
// We expected it to catch up by running callbacks for all missed symbols
|
||||
// Let's verify the number of symbols it executed.
|
||||
printf("After forward jump, processed %d callbacks.\n", tracker.count);
|
||||
if (tracker.count < 5) {
|
||||
fprintf(stderr, "FAIL: forward jump did not trigger catch up callbacks (count=%d)\n", tracker.count);
|
||||
return 1;
|
||||
}
|
||||
// Let's verify the sequence of symbols is contiguous
|
||||
for (int i = 0; i < tracker.count; i++) {
|
||||
printf("Callback %d: Symbol %lu\n", i, tracker.symbols[i]);
|
||||
if (i > 0 && tracker.symbols[i] != tracker.symbols[i - 1] + 1) {
|
||||
fprintf(stderr, "FAIL: non-contiguous symbol sequence in catch up\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
printf("Test Case 3 passed: Forward jump catch up works.\n");
|
||||
|
||||
// --- Test Case 4: Clock Jump Backward (PTP Sync Backward) ---
|
||||
// Reset tracker
|
||||
tracker.count = 0;
|
||||
// Get current state
|
||||
uint64_t pre_jump_next = timer.next_s_abs;
|
||||
uint64_t pre_jump_target = timer.target_gps_ns;
|
||||
printf("Before backward jump: next_s_abs=%lu, target_gps_ns=%lu\n", pre_jump_next, pre_jump_target);
|
||||
|
||||
// Jump mock time backward to tv_nsec = 100000
|
||||
mock_time.tv_nsec = 100000;
|
||||
fh_timer_tick(&timer);
|
||||
if (tracker.count != 0) {
|
||||
fprintf(stderr, "FAIL: callbacks executed during backward clock jump (count=%d)\n", tracker.count);
|
||||
return 1;
|
||||
}
|
||||
// Move time forward a bit but still below target
|
||||
mock_time.tv_nsec = 200000;
|
||||
fh_timer_tick(&timer);
|
||||
if (tracker.count != 0) {
|
||||
fprintf(stderr, "FAIL: callbacks executed before catching up to previous state (count=%d)\n", tracker.count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Now move time past the old target
|
||||
mock_time = gps_ns_to_timespec(pre_jump_target + 1000);
|
||||
|
||||
printf("Setting mock time past old target: tv_sec=%ld, tv_nsec=%ld\n", mock_time.tv_sec, mock_time.tv_nsec);
|
||||
fh_timer_tick(&timer);
|
||||
if (tracker.count == 0) {
|
||||
fprintf(stderr, "FAIL: timer did not resume after clock caught up to previous target\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Resumed successfully with symbol %lu\n", tracker.symbols[0]);
|
||||
if (tracker.symbols[0] != pre_jump_next) {
|
||||
fprintf(stderr, "FAIL: resumed symbol %lu does not match expected %lu\n", tracker.symbols[0], pre_jump_next);
|
||||
return 1;
|
||||
}
|
||||
printf("Test Case 4 passed: Backward jump (PTP sync backward) handling works\n");
|
||||
|
||||
// --- Test Case 5: fh_timer_get_current_symbol ---
|
||||
mock_time.tv_sec = 1718000000;
|
||||
mock_time.tv_nsec = 500000; // 0.5 ms
|
||||
uint64_t cur_sym = fh_timer_get_current_symbol(&timer);
|
||||
typedef __int128_t int128;
|
||||
uint64_t expected_sym =
|
||||
(uint64_t)((((int128)1718000000 - GPS_EPOCH_OFFSET_UNIX + GPS_LEAP_SECONDS) * NS_PER_SEC + 500000) * 28 / 1000000);
|
||||
if (cur_sym != expected_sym) {
|
||||
fprintf(stderr, "FAIL: fh_timer_get_current_symbol returned %lu, expected %lu\n", cur_sym, expected_sym);
|
||||
return 1;
|
||||
}
|
||||
printf("Test Case 5 passed: fh_timer_get_current_symbol works.\n");
|
||||
|
||||
printf("ALL TESTS PASSED SUCCESSFULLY!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -255,7 +255,7 @@ int oru_fh_get_utc_anchor_point(void *handle, uint32_t *frame, uint32_t *slot, s
|
||||
uint64_t total_syms_per_sec = (NR_SYMBOLS_PER_SLOT * 1000) << fh->cfg.numerology;
|
||||
uint64_t ns_per_symbol = 1000000000 / total_syms_per_sec;
|
||||
uint64_t leftover_syms = absolute_gps_symbol % total_syms_per_sec;
|
||||
ts->tv_sec = (absolute_gps_symbol / total_syms_per_sec) + GPS_EPOCH_OFFSET_UNIX;
|
||||
ts->tv_sec = (absolute_gps_symbol / total_syms_per_sec) + GPS_EPOCH_OFFSET_UNIX - GPS_LEAP_SECONDS;
|
||||
ts->tv_nsec = leftover_syms * ns_per_symbol;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "common/platform_types.h"
|
||||
#include "xran_pkt_api.h"
|
||||
#include "oru_packet_processor.h"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
set -e
|
||||
|
||||
# This script downloads a PCAP file, unarchives it, and then runs a test executable.
|
||||
|
||||
Reference in New Issue
Block a user