Compare commits

...

9 Commits

Author SHA1 Message Date
Ming-Hong HSU, BMW Lab@NTUST
1db365ed89 feat(nfapi/vnf): implement Delay management for dynamic slot timing (EWMA-based)
Changes:

* Add comprehensive delay management (DM) and EWMA state variables (e.g., jitter estimates, risk debts, pressure holds, and safe margins) to `nfapi_vnf_p7_connection_info_t` in `vnf_p7.h`.
* Introduce integer-based EWMA math helpers (`p7_ewma_step_i32`, `calculate_slot_distance`, etc.) in `vnf_p7.c` to handle precise timing calculations and prevent integer dead-zones.
* Implement the core `vnf_nr_delay_management()` controller, which dynamically computes the optimal `slot_ahead` using a fast-attack/slow-release pressure model, risk/debt memory, and critical cliff region mapping.
* Integrate the delay controller into `vnf_nr_handle_timing_info()`, feeding it the timing statistics parsed from the PNF to continuously adjust pacing.
* Clean up extensive trailing whitespaces and formatting inconsistencies throughout `vnf_p7.c`.

Purpose:
To establish a robust, adaptive dynamic slot timing controller for the VNF. By leveraging an EWMA-based algorithm, the VNF can continuously track network jitter, estimate mean delays, and intelligently balance timing "debt" against "safe margins." This enables the VNF to proactively and smoothly adjust its transmission pacing (`slot_ahead`) to prevent late packet drops under varying network conditions, eliminating the abrupt and erratic timing jumps seen in the legacy sync implementation.

Signed-off-by: Ming-Hong HSU, BMW Lab@NTUST <m11302209@gapps.ntust.edu.tw>
2026-05-24 15:41:26 +08:00
Ming-Hong HSU, BMW Lab@NTUST
447b592924 feat(nfapi/vnf): implement autonomous VNF timing thread and timing info extraction
Changes:

* **common/utils/system.c**: Change `pthread_setname_np` failure behavior from `AssertFatal` to a non-fatal `LOG_E` to prevent unnecessary crashes on thread creation.
* **nfapi_vnf.c / vnf_p7.c**: Introduce a dedicated `vnf_timing_thread` to autonomously drive the VNF slot timing using high-resolution `clock_nanosleep`.
* **vnf_p7.c**: Implement `vnf_nr_extract_timing_info()` to parse incoming PNF timing information and calculate dynamic statistics (worst late, worst early, and max jitter) instead of abruptly overriding SFN/slot.
* **vnf_p7_interface.c / vnf_p7.h**: Add thread-safe state management (`mutex`, `pthread_cond_t`) to ensure the timing thread waits for `initial_timinginfo_received` before starting its autonomous loop.
* **nr_fapi_p5.c / nfapi_vnf.c**: Add parsing and packing for P7 timing offset TLVs (`DL_TTI`, `UL_TTI`, `UL_DCI`, `TX_DATA`) in the P5 Param Response.
* **nfapi_vnf.c**: Update default P7 configuration, setting the `timing_window` to 4500 and enabling `periodic_timing`.

Purpose:
Previously, the VNF SFN/Slot advancement was tightly coupled to PNF timing indications, making the system highly sensitive to network jitter and resulting in unstable SFN/slot jumps when large delays occurred. By introducing an independent, autonomous timing thread on the VNF side, the system can now maintain a stable local slot clock while applying periodic synchronizations and burst catch-up mechanisms. Additionally, extracting comprehensive delay and jitter statistics from the PNF enables the upcoming dynamic slot sleep timing controller to make precise, mathematically sound adjustments without causing sudden timing disruptions.

Signed-off-by: Ming-Hong HSU, BMW Lab@NTUST <m11302209@gapps.ntust.edu.tw>
2026-05-24 15:41:26 +08:00
Ming-Hong HSU, BMW Lab@NTUST
9878057726 feat(nfapi/vnf): implement dynamic slot sleep timing control and refactor UL node sync
Changes:

* Fix unused variable warning in `nfapi_pnf.c` by scoping the `nfapi_nr_slot_indication_scf_t` definition inside the `#ifdef ENABLE_WLS` block.
* Add synchronization state variables (`slot_adjustment`, `us_adjustment`, `sync_locked`, `slot_duration_us`) and a thread-safe mutex to the `nfapi_vnf_p7_connection_info` struct in `vnf_p7.h`.
* Introduce dynamic target margin constants (`MARGIN_TOLERANCE_US`, `MARGIN_TOLERANCE_LOCKED_US`) to establish an adaptive deadband zone.
* Implement `timehr_diff_us()` in `vnf_p7.c` to correctly calculate signed microsecond differences while handling 12-bit second wrap-arounds (4096 seconds).
* Completely rewrite `vnf_nr_handle_ul_node_sync()` to calculate timing offset using the standard symmetric delay formula: `((T2 - T1) - (T4 - T3)) / 2`.
* Add 10.24s wrap-around protection to the offset calculation.
* Implement drift monitoring logic that stops continuous adjustments once synchronized, but automatically unlocks for re-calibration if the offset exceeds `MARGIN_TOLERANCE_LOCKED_US`.

Purpose:
The legacy UL node sync algorithm was overly complex, relying on cycle counts, moving averages, and trend filtering that were prone to instability, especially during timestamp wrap-arounds. The refactored synchronization algorithm simplifies the offset calculation, makes it mathematically rigorous, and handles 10.24s nFAPI timestamp limits safely. By introducing locked and unlocked tolerance margins (deadband zones), the system avoids thrashing with continuous micro-adjustments, ensuring more stable P7 slot timing. The minor change in `nfapi_pnf.c` resolves a compilation warning when WLS is not enabled.

Signed-off-by: Ming-Hong HSU, BMW Lab@NTUST <m11302209@gapps.ntust.edu.tw>
2026-05-24 15:41:21 +08:00
Ming-Hong HSU, BMW Lab@NTUST
ffb3020014 fix(nfapi/pnf): prevent duplica te slot increment in P7 message pump
Shouldn't have duplicate slot increment at PNF side

Signed-off-by: Ming-Hong HSU, BMW Lab@NTUST <m11302209@gapps.ntust.edu.tw>
2026-05-24 15:39:25 +08:00
Ming-Hong HSU, BMW Lab@NTUST
d61244943d feat(nfapi/pnf): overhaul P7 timing measurement and jitter calculation
Rewrite the PNF-side timing infrastructure to fix wrap-around bugs,
implement RFC 3550 jitter, and unify per-message timing checks:

TIMEHR wrap-around fix (get_slot_time):
- Add timehr_diff_us() computing a signed difference that correctly
  handles the 12-bit second field wrap-around (~4096 s cycle)
- Rewrite get_slot_time() using timehr_diff_us; remove the previous
  implementation that mis-handled wrap-around

RFC 3550 jitter calculation (pnf_p7.c / pnf_p7.h):
- Introduce nfapi_jitter_msg_type_t enum for DL_TTI, UL_TTI, UL_DCI,
  TX_DATA message types
- Add pnf_timehr_to_us(), pnf_update_jitter(), pnf_get_jitter(),
  pnf_reset_jitter() implementing the RFC 3550 inter-arrival jitter
  formula
- Call pnf_update_jitter() on each received P7 message
- pnf_p7.h: add jitter state fields and function declarations

Unified P7 timing check (check_nr_p7_timing):
- Add calc_slot_diff() helper for wrap-aware slot number comparison
- Introduce check_nr_p7_timing() replacing four separate copies of
  the per-message timing validation; it computes margin, updates
  latest_delay / earliest_arrival, logs out-of-window events, and
  triggers aperiodic timing info via a flag rather than a direct send
- Add timing_info_aperiodic_send flag, timing_info_trigger_sfn/slot,
  timing_info_last_send_time_hr, timing_window and per-message
  timing_offset fields to pnf_p7_t

Fix pnf_nr_pack_and_send_timing_info:
- Compute last_sfn/last_slot as the previous slot per SCF 225
- Calculate elapsed time using timing_info_last_send_time_hr
- Populate latest_delay / earliest_arrival from tracked stats
- Use renamed tx_data_jitter (not tx_data_request_jitter)

pnf_p7_interface.c: propagate timing_window and per-message offsets
from pnf_p7_t during P7 start

Signed-off-by: dong881 <minghunghsu.taiwan@gmail.com>

fix(nfapi/pnf): stop sending slot_indication from PNF; remove duplicate slot counter

Per SCF 225, the slot.indication message originates at the VNF and flows
down to the PNF. The PNF must not echo it back.

- nfapi_pnf.c (handle_nr_slot_ind): comment out oai_nfapi_nr_slot_indication()
  call; the VNF drives slot timing autonomously via its own timing thread
- nfapi_pnf.c (nr_start_resp_cb): set nr_start_resp_received flag so that
  the VNF can track when the PNF has started
- socket_pnf.c (pnf_nr_p7_message_pump): remove the duplicate sfn/slot
  increment inside the message pump loop; slot counting is handled by the
  PNF processing path and must not be done twice

Signed-off-by: Ming-Hong HSU, BMW Lab@NTUST <m11302209@gapps.ntust.edu.tw>
2026-05-24 15:39:25 +08:00
Ming-Hong HSU, BMW Lab@NTUST
6c8093b9a6 feat(nfapi): add per-message P7 timing offset TLVs in PARAM/CONFIG
Implement SCF225 timing offset TLV handling for DL_TTI, UL_TTI, UL_DCI and
TX_DATA in request/response pack-unpack and runtime config propagation.
This enables message-specific timing offset negotiation between VNF and PNF.

Signed-off-by: Ming-Hong HSU, BMW Lab@NTUST <m11302209@gapps.ntust.edu.tw>
2026-05-24 15:39:25 +08:00
Ming-Hong HSU, BMW Lab@NTUST
21501ea2f8 fix(nfapi/vnf): make timing window/mode/period runtime configurable
Stop hardcoding timing_window, timing_info_mode, and timing_info_period in
VNF PHY allocation. Initialize defaults in vnf config and propagate configured
values during VNF setup so deployments can tune timing behavior without code edits.

Signed-off-by: Ming-Hong HSU, BMW Lab@NTUST <m11302209@gapps.ntust.edu.tw>
2026-05-24 15:39:25 +08:00
Ming-Hong HSU, BMW Lab@NTUST
94269ddca1 fix(nfapi): align tx_data timing field names with SCF 225
Rename tx_data_request_* timing fields to tx_data_* in NR timing-related
structures and handling paths. This removes naming ambiguity and aligns
with current SCF 225 terminology used by timing info exchange.

Signed-off-by: Ming-Hong HSU, BMW Lab@NTUST <m11302209@gapps.ntust.edu.tw>
2026-05-24 15:39:25 +08:00
Ming-Hong HSU, BMW Lab@NTUST
c42bb45be9 fix(nfapi): widen timing_window field to uint16 for SCF 225 range
SCF 225 allows timing_window up to 30000us, which exceeds uint8 capacity.
Promote timing_window-related fields and pack/unpack paths to uint16 so
configuration values are represented consistently across VNF/PNF interfaces.

Signed-off-by: Ming-Hong HSU, BMW Lab@NTUST <m11302209@gapps.ntust.edu.tw>
2026-05-24 15:39:25 +08:00
18 changed files with 2159 additions and 661 deletions

View File

@@ -270,8 +270,9 @@ void threadCreate(pthread_t* t, void * (*func)(void*), void * param, char* name,
strncpy(short_name, name, sizeof(short_name) - 1);
short_name[sizeof(short_name) - 1] = '\0';
ret = pthread_setname_np(*t, short_name);
AssertFatal(ret == 0, "Error in pthread_setname_np(): ret: %d, errno: %d\n", ret, errno);
if (ret != 0) {
LOG_E(UTIL, "Error in pthread_setname_np() for %s: ret: %d, errno: %d\n", short_name, ret, errno);
}
if (affinity != -1 ) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);

View File

@@ -121,9 +121,13 @@ typedef struct {
uint8_t first_subframe_ind;
// timing information recevied from the vnf
uint8_t timing_window;
uint16_t timing_window;
uint8_t timing_info_mode;
uint8_t timing_info_period;
uint32_t dl_tti_timing_offset;
uint32_t ul_tti_timing_offset;
uint32_t ul_dci_timing_offset;
uint32_t tx_data_timing_offset;
} phy_info;
@@ -991,7 +995,22 @@ int nr_config_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, n
phy_info->timing_info_mode = 0;
printf("NO timing info mode provided\n");
}
// TODO: Read the P7 message offset values
if (req->nfapi_config.dl_tti_timing_offset.tl.tag == NFAPI_NR_NFAPI_DL_TTI_TIMING_OFFSET) {
phy_info->dl_tti_timing_offset = req->nfapi_config.dl_tti_timing_offset.value;
num_tlv++;
}
if (req->nfapi_config.ul_tti_timing_offset.tl.tag == NFAPI_NR_NFAPI_UL_TTI_TIMING_OFFSET) {
phy_info->ul_tti_timing_offset = req->nfapi_config.ul_tti_timing_offset.value;
num_tlv++;
}
if (req->nfapi_config.ul_dci_timing_offset.tl.tag == NFAPI_NR_NFAPI_UL_DCI_TIMING_OFFSET) {
phy_info->ul_dci_timing_offset = req->nfapi_config.ul_dci_timing_offset.value;
num_tlv++;
}
if (req->nfapi_config.tx_data_timing_offset.tl.tag == NFAPI_NR_NFAPI_TX_DATA_TIMING_OFFSET) {
phy_info->tx_data_timing_offset = req->nfapi_config.tx_data_timing_offset.value;
num_tlv++;
}
if (req->nfapi_config.timing_info_period.tl.tag == NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG) {
printf("timing info period provided value:%d\n", req->nfapi_config.timing_info_period.value);
phy_info->timing_info_period = req->nfapi_config.timing_info_period.value;
@@ -1630,10 +1649,13 @@ int nr_start_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nf
p7_config->subframe_buffer_size = phy_info->timing_window;
p7_config->slot_buffer_size = phy_info->timing_window; // TODO: check if correct for NR
printf("subframe_buffer_size configured using phy_info->timing_window:%d\n", phy_info->timing_window);
// Reset timing info defaults from nfapi_pnf_p7_config_create, use VNF config values instead
p7_config->timing_info_mode_periodic = 0;
p7_config->timing_info_mode_aperiodic = 0;
p7_config->timing_info_period = phy_info->timing_info_period;
if (phy_info->timing_info_mode & 0x1) {
p7_config->timing_info_mode_periodic = 1;
p7_config->timing_info_period = phy_info->timing_info_period;
}
if (phy_info->timing_info_mode & 0x2) {
@@ -1722,6 +1744,11 @@ int nr_start_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nf
DevAssert(scs->tl.tag == NFAPI_NR_CONFIG_SCS_COMMON_TAG);
pnf_p7_t* pnf_p7 = (pnf_p7_t*)(p7_config);
pnf_p7->mu = scs->value;
pnf_p7->timing_window = phy_info->timing_window;
pnf_p7->dl_tti_timing_offset = phy_info->dl_tti_timing_offset;
pnf_p7->ul_tti_timing_offset = phy_info->ul_tti_timing_offset;
pnf_p7->ul_dci_timing_offset = phy_info->ul_dci_timing_offset;
pnf_p7->tx_data_timing_offset = phy_info->tx_data_timing_offset;
// Need to wait for main thread to create RU structures
while (config_sync_var < 0) {
@@ -2304,8 +2331,10 @@ void handle_nr_slot_ind(uint16_t sfn, uint16_t slot, NR_Sched_Rsp_t *sched_resp)
sfnslot_add_slot(mu, &sfn_tx, &slot_tx, slot_ahead); // modify: do in place
// printf("send slot indication for sfn/slot:%4d.%2d current:%4d.%2d\n", sfn_tx, slot_tx, sfn, slot);
#ifdef ENABLE_WLS
nfapi_nr_slot_indication_scf_t ind = {.sfn = sfn_tx, .slot = slot_tx};
oai_nfapi_nr_slot_indication(&ind);
#endif
// copy data from appropriate p7 slot buffers into channel structures for PHY processing
nfapi_pnf_p7_get_msgs(config,

View File

@@ -56,6 +56,8 @@ static nfapi_vnf_config_t *config;
extern RAN_CONTEXT_t RC;
extern UL_RCC_IND_t UL_RCC_INFO;
static volatile int nr_start_resp_received = 0;
nfapi_vnf_config_t * get_config()
{
return config;
@@ -941,6 +943,150 @@ int phy_nr_slot_indication(nfapi_nr_slot_indication_scf_t *ind)
return 1;
}
#ifndef ENABLE_WLS
static inline void timespec_add_us(struct timespec *t, long us)
{
t->tv_nsec += us * 1000;
if (t->tv_nsec >= 1000000000) {
t->tv_sec += t->tv_nsec / 1000000000;
t->tv_nsec %= 1000000000;
} else if (t->tv_nsec < 0) {
long sec_diff = (-t->tv_nsec / 1000000000) + 1;
t->tv_sec -= sec_diff;
t->tv_nsec += sec_diff * 1000000000;
}
}
#define P7_SYNC_PERIOD_SLOTS_DEFAULT 2000
#define P7_SYNC_MAX_CATCHUP_BURST 2
int vnf_nr_build_send_dl_node_sync(vnf_p7_t* vnf_p7, nfapi_vnf_p7_connection_info_t* p7_info);
static inline void p7_sync_init(nfapi_vnf_p7_connection_info_t *p7_info)
{
p7_info->sync_slot_counter = 0;
p7_info->sync_period_slots = P7_SYNC_PERIOD_SLOTS_DEFAULT;
NFAPI_TRACE(NFAPI_TRACE_INFO, "[P7_SYNC] Initialized: period=%u slots\n",
p7_info->sync_period_slots);
}
void *vnf_timing_thread(void *arg)
{
LOG_I(NFAPI_VNF, "Starting VNF autonomous timing thread\n");
vnf_p7_info *p7_vnf = (vnf_p7_info *)arg;
vnf_p7_t *vnf_p7 = (vnf_p7_t *)p7_vnf->config;
int mu = -1;
nfapi_vnf_p7_connection_info_t *p7_info = NULL;
while (1) {
if (nr_start_resp_received) {
if (vnf_p7->p7_connections) {
p7_info = vnf_p7->p7_connections;
if (RC.nrmac && RC.nrmac[0]) {
nfapi_nr_config_request_scf_t *req = &RC.nrmac[0]->config[0];
const nfapi_uint8_tlv_t *scs = &req->ssb_config.scs_common;
if (scs && scs->tl.tag == NFAPI_NR_CONFIG_SCS_COMMON_TAG) {
mu = scs->value;
}
}
if (mu < 0 && RC.gNB && RC.gNB[0] && RC.gNB[0]->configured && RC.gNB[0]->frame_parms.numerology_index >= 0) {
mu = RC.gNB[0]->frame_parms.numerology_index;
}
if (mu >= 0)
break;
}
}
usleep(1000000);
LOG_I(NFAPI_VNF, "Waiting for gNB or NFAPI NR configuration... mu:%d start_resp:%d\n", mu, nr_start_resp_received);
}
pthread_mutex_lock(&p7_info->mutex);
while (!p7_info->initial_timinginfo_received) {
pthread_cond_wait(&p7_info->initial_timinginfo_cond, &p7_info->mutex);
}
pthread_mutex_unlock(&p7_info->mutex);
AssertFatal(mu >= 0 && mu <= 5, "Invalid mu %d\n", mu);
p7_info->mu = mu;
p7_info->slot_duration_us = 1000 >> p7_info->mu;
if (p7_info->initial_timinginfo_received) {
int sfnslot_dec = NFAPI_SFNSLOT2DEC(p7_info->mu, p7_info->sfn, p7_info->slot);
sfnslot_dec = (sfnslot_dec + 1) % NFAPI_MAX_SFNSLOTDEC(p7_info->mu);
p7_info->sfn = NFAPI_SFNSLOTDEC2SFN(p7_info->mu, sfnslot_dec);
p7_info->slot = NFAPI_SFNSLOTDEC2SLOT(p7_info->mu, sfnslot_dec);
}
p7_info->running = 1;
p7_info->thread = pthread_self();
p7_sync_init(p7_info);
clock_gettime(CLOCK_MONOTONIC, &p7_info->next_slot_time);
vnf_p7->slot_start_time_hr = vnf_get_current_time_hr();
vnf_nr_build_send_dl_node_sync(vnf_p7, p7_info);
const int max_sfnslotdec = NFAPI_MAX_SFNSLOTDEC(p7_info->mu);
const int extreme_gap_threshold = max_sfnslotdec / 4;
int last_mac_ind_dec = -1;
int sfnslot_dec = NFAPI_SFNSLOT2DEC(p7_info->mu, p7_info->sfn, p7_info->slot);
while (p7_info->running) {
pthread_mutex_lock(&p7_info->mutex);
if (p7_info->slot_adjustment != 0) {
sfnslot_dec = (sfnslot_dec + p7_info->slot_adjustment + max_sfnslotdec) % max_sfnslotdec;
p7_info->slot_adjustment = 0;
}
int32_t current_pending_us = p7_info->pending_us;
p7_info->pending_us = 0;
pthread_mutex_unlock(&p7_info->mutex);
timespec_add_us(&p7_info->next_slot_time, p7_info->slot_duration_us + current_pending_us);
if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &p7_info->next_slot_time, NULL) != 0)
continue;
vnf_p7->slot_start_time_hr = vnf_get_current_time_hr();
p7_info->sfn = NFAPI_SFNSLOTDEC2SFN(p7_info->mu, sfnslot_dec);
p7_info->slot = NFAPI_SFNSLOTDEC2SLOT(p7_info->mu, sfnslot_dec);
if (p7_info->sync_slot_counter >= p7_info->sync_period_slots) {
p7_info->sync_slot_counter = 0;
vnf_nr_build_send_dl_node_sync(vnf_p7, p7_info);
} else {
p7_info->sync_slot_counter++;
}
int target_ind_dec = (sfnslot_dec + p7_info->slot_ahead) % max_sfnslotdec;
if (last_mac_ind_dec == -1) {
last_mac_ind_dec = (target_ind_dec - 1 + max_sfnslotdec) % max_sfnslotdec;
}
int diff_mac = (target_ind_dec - last_mac_ind_dec + max_sfnslotdec) % max_sfnslotdec;
if (diff_mac > 0 && diff_mac < max_sfnslotdec / 2) {
if (!p7_info->sync_locked || diff_mac > extreme_gap_threshold) {
if (p7_info->sync_locked) {
NFAPI_TRACE(NFAPI_TRACE_WARN, "[P7_SYNC] Extreme VNF gap (%d slots). Jumping to latest to avoid deadlock.\n", diff_mac);
}
last_mac_ind_dec = target_ind_dec;
nfapi_nr_slot_indication_scf_t ind = {0};
ind.sfn = NFAPI_SFNSLOTDEC2SFN(p7_info->mu, last_mac_ind_dec);
ind.slot = NFAPI_SFNSLOTDEC2SLOT(p7_info->mu, last_mac_ind_dec);
ind.header.phy_id = p7_info->phy_id;
phy_nr_slot_indication(&ind);
} else {
int burst_counter = 0;
while (last_mac_ind_dec != target_ind_dec && burst_counter < P7_SYNC_MAX_CATCHUP_BURST) {
last_mac_ind_dec = (last_mac_ind_dec + 1) % max_sfnslotdec;
nfapi_nr_slot_indication_scf_t ind = {0};
ind.sfn = NFAPI_SFNSLOTDEC2SFN(p7_info->mu, last_mac_ind_dec);
ind.slot = NFAPI_SFNSLOTDEC2SLOT(p7_info->mu, last_mac_ind_dec);
ind.header.phy_id = p7_info->phy_id;
phy_nr_slot_indication(&ind);
burst_counter++;
}
}
}
sfnslot_dec = (sfnslot_dec + 1) % max_sfnslotdec;
}
return NULL;
}
#endif
int phy_nr_srs_indication(nfapi_nr_srs_indication_t *ind)
{
for (int i = 0; i < ind->number_of_pdus; ++i)
@@ -1264,6 +1410,11 @@ void *configure_nr_p7_vnf(void *ptr)
p7_vnf->config->hdr_unpack_func = &fapi_nr_p7_message_header_unpack;
p7_vnf->config->pack_func = &fapi_nr_p7_message_pack;
p7_vnf->config->send_p7_msg = &aerial_nr_send_p7_message;
#endif
#ifndef ENABLE_WLS
// Start VNF autonomous timing thread
pthread_t t;
threadCreate(&t, &vnf_timing_thread, p7_vnf, "vnf_timing", -1, OAI_PRIORITY_RT);
#endif
return 0;
}
@@ -1298,7 +1449,7 @@ void *vnf_p7_thread_start(void *ptr) {
p7_vnf->config->codec_config.deallocate = &vnf_deallocate;
p7_vnf->config->allocate_p7_vendor_ext = &phy_allocate_p7_vendor_ext;
p7_vnf->config->deallocate_p7_vendor_ext = &phy_deallocate_p7_vendor_ext;
NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Creating VNF NFAPI P7 start thread %s\n", __FUNCTION__);
NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Creating VNF NFAPI start thread %s\n", __FUNCTION__);
pthread_create(&vnf_p7_start_pthread, NULL, &vnf_p7_start_thread, p7_vnf->config);
return 0;
}
@@ -1418,11 +1569,20 @@ int nr_param_resp_cb(nfapi_vnf_config_t *config, int p5_idx, nfapi_nr_param_resp
req->num_tlv++;
}
}
//TODO: Assign tag and value for P7 message offsets
req->nfapi_config.dl_tti_timing_offset.tl.tag = NFAPI_NR_NFAPI_DL_TTI_TIMING_OFFSET;
req->nfapi_config.ul_tti_timing_offset.tl.tag = NFAPI_NR_NFAPI_UL_TTI_TIMING_OFFSET;
req->nfapi_config.ul_dci_timing_offset.tl.tag = NFAPI_NR_NFAPI_UL_DCI_TIMING_OFFSET;
req->nfapi_config.tx_data_timing_offset.tl.tag = NFAPI_NR_NFAPI_TX_DATA_TIMING_OFFSET;
// Assign tag and value for P7 message offsets
req->nfapi_config.dl_tti_timing_offset.tl.tag = NFAPI_NR_NFAPI_DL_TTI_TIMING_OFFSET;
req->nfapi_config.dl_tti_timing_offset.value = p7_vnf->dl_tti_timing_offset;
req->nfapi_config.ul_tti_timing_offset.tl.tag = NFAPI_NR_NFAPI_UL_TTI_TIMING_OFFSET;
req->nfapi_config.ul_tti_timing_offset.value = p7_vnf->ul_tti_timing_offset;
req->nfapi_config.ul_dci_timing_offset.tl.tag = NFAPI_NR_NFAPI_UL_DCI_TIMING_OFFSET;
req->nfapi_config.ul_dci_timing_offset.value = p7_vnf->ul_dci_timing_offset;
req->nfapi_config.tx_data_timing_offset.tl.tag = NFAPI_NR_NFAPI_TX_DATA_TIMING_OFFSET;
req->nfapi_config.tx_data_timing_offset.value = p7_vnf->tx_data_timing_offset;
req->num_tlv += 4;
vendor_ext_tlv_2 ve2;
memset(&ve2, 0, sizeof(ve2));
@@ -1526,6 +1686,7 @@ int start_resp_cb(nfapi_vnf_config_t *config, int p5_idx, nfapi_start_response_t
int nr_start_resp_cb(nfapi_vnf_config_t *config, int p5_idx, nfapi_nr_start_response_scf_t *resp) {
NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Received NFAPI_START_RESP idx:%d phy_id:%d\n", p5_idx, resp->header.phy_id);
nr_start_resp_received = 1;
return 0;
}
@@ -1704,8 +1865,12 @@ void configure_nr_nfapi_vnf(eth_params_t params)
#endif
vnf_info *vnf = calloc(1, sizeof(vnf_info));
memset(vnf->p7_vnfs, 0, sizeof(vnf->p7_vnfs));
vnf->p7_vnfs[0].timing_window = 30;
vnf->p7_vnfs[0].periodic_timing_enabled = 0;
vnf->p7_vnfs[0].timing_window = 4500;
vnf->p7_vnfs[0].dl_tti_timing_offset = 0;
vnf->p7_vnfs[0].ul_tti_timing_offset = 0;
vnf->p7_vnfs[0].ul_dci_timing_offset = 0;
vnf->p7_vnfs[0].tx_data_timing_offset = 0;
vnf->p7_vnfs[0].periodic_timing_enabled = 1;
vnf->p7_vnfs[0].aperiodic_timing_enabled = 0;
vnf->p7_vnfs[0].periodic_timing_period = 1;
vnf->p7_vnfs[0].config = nfapi_vnf_p7_config_create();
@@ -1729,6 +1894,9 @@ void configure_nr_nfapi_vnf(eth_params_t params)
config->vnf_ipv6 = 0;
config->pnf_list = 0;
config->phy_list = 0;
config->timing_window = vnf->p7_vnfs[0].timing_window;
config->timing_info_mode = (vnf->p7_vnfs[0].aperiodic_timing_enabled << 1) | (vnf->p7_vnfs[0].periodic_timing_enabled);
config->timing_info_period = vnf->p7_vnfs[0].periodic_timing_period;
config->pnf_nr_connection_indication = &pnf_nr_connection_indication_cb;
config->pnf_disconnect_indication = &pnf_disconnection_indication_cb;

View File

@@ -42,7 +42,7 @@ typedef struct {
uint8_t first_subframe_ind;
// timing information recevied from the vnf
uint8_t timing_window;
uint16_t timing_window;
uint8_t timing_info_mode;
uint8_t timing_info_period;
@@ -104,10 +104,14 @@ typedef struct {
int local_port;
char local_addr[80];
unsigned timing_window;
uint16_t timing_window;
unsigned periodic_timing_enabled;
unsigned aperiodic_timing_enabled;
unsigned periodic_timing_period;
uint32_t dl_tti_timing_offset;
uint32_t ul_tti_timing_offset;
uint32_t ul_dci_timing_offset;
uint32_t tx_data_timing_offset;
// This is not really the right place if we have multiple PHY,
// should be part of the phy struct

View File

@@ -799,12 +799,6 @@ int pnf_nr_p7_message_pump(pnf_p7_t *pnf_p7)
// update slot start timing
slot_start = pnf_timespec_add(slot_start, slot_duration);
// increment sfn/slot
if (++pnf_p7->slot == 20) {
pnf_p7->slot = 0;
pnf_p7->sfn = (pnf_p7->sfn + 1) % 1024;
}
continue;
} else if (selectRetval == -1 && (errno == EINTR)) {
// interrupted by signal

View File

@@ -535,7 +535,7 @@ uint8_t pack_nr_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *e
&(pNfapiMsg->nfapi_config.timing_window),
ppWritePackedMsg,
end,
&pack_uint8_tlv_value)
&pack_uint16_tlv_value)
&& pack_nr_tlv(NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG,
&(pNfapiMsg->nfapi_config.timing_info_mode),
ppWritePackedMsg,
@@ -546,6 +546,26 @@ uint8_t pack_nr_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *e
ppWritePackedMsg,
end,
&pack_uint8_tlv_value)
&& pack_nr_tlv(NFAPI_NR_NFAPI_DL_TTI_TIMING_OFFSET,
&(pNfapiMsg->nfapi_config.dl_tti_timing_offset),
ppWritePackedMsg,
end,
&pack_uint32_tlv_value)
&& pack_nr_tlv(NFAPI_NR_NFAPI_UL_TTI_TIMING_OFFSET,
&(pNfapiMsg->nfapi_config.ul_tti_timing_offset),
ppWritePackedMsg,
end,
&pack_uint32_tlv_value)
&& pack_nr_tlv(NFAPI_NR_NFAPI_UL_DCI_TIMING_OFFSET,
&(pNfapiMsg->nfapi_config.ul_dci_timing_offset),
ppWritePackedMsg,
end,
&pack_uint32_tlv_value)
&& pack_nr_tlv(NFAPI_NR_NFAPI_TX_DATA_TIMING_OFFSET,
&(pNfapiMsg->nfapi_config.tx_data_timing_offset),
ppWritePackedMsg,
end,
&pack_uint32_tlv_value)
&& pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config);
return retval;
}
@@ -692,9 +712,13 @@ uint8_t unpack_nr_param_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *
{NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv4, &unpack_ipv4_address_value},
{NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv6, &unpack_ipv6_address_value},
{NFAPI_NR_NFAPI_P7_PNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_pnf_port, &unpack_uint16_tlv_value},
{NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &pNfapiMsg->nfapi_config.timing_window, &unpack_uint8_tlv_value},
{NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &pNfapiMsg->nfapi_config.timing_window, &unpack_uint16_tlv_value},
{NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG, &pNfapiMsg->nfapi_config.timing_info_mode, &unpack_uint8_tlv_value},
{NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, &pNfapiMsg->nfapi_config.timing_info_period, &unpack_uint8_tlv_value},
{NFAPI_NR_NFAPI_DL_TTI_TIMING_OFFSET, &(pNfapiMsg->nfapi_config.dl_tti_timing_offset), &unpack_uint32_tlv_value},
{NFAPI_NR_NFAPI_UL_TTI_TIMING_OFFSET, &(pNfapiMsg->nfapi_config.ul_tti_timing_offset), &unpack_uint32_tlv_value},
{NFAPI_NR_NFAPI_UL_DCI_TIMING_OFFSET, &(pNfapiMsg->nfapi_config.ul_dci_timing_offset), &unpack_uint32_tlv_value},
{NFAPI_NR_NFAPI_TX_DATA_TIMING_OFFSET, &(pNfapiMsg->nfapi_config.tx_data_timing_offset), &unpack_uint32_tlv_value},
};
return (pull8(ppReadPackedMsg, &pNfapiMsg->error_code, end) && pull8(ppReadPackedMsg, &pNfapiMsg->num_tlv, end)
@@ -1191,7 +1215,7 @@ uint8_t pack_nr_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *e
&(pNfapiMsg->nfapi_config.timing_window),
ppWritePackedMsg,
end,
&pack_uint8_tlv_value);
&pack_uint16_tlv_value);
numTLVs++;
retval &= pack_nr_tlv(NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG,
@@ -1207,6 +1231,34 @@ uint8_t pack_nr_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *e
end,
&pack_uint8_tlv_value);
numTLVs++;
retval &= pack_nr_tlv(NFAPI_NR_NFAPI_DL_TTI_TIMING_OFFSET,
&(pNfapiMsg->nfapi_config.dl_tti_timing_offset),
ppWritePackedMsg,
end,
&pack_uint32_tlv_value);
numTLVs++;
retval &= pack_nr_tlv(NFAPI_NR_NFAPI_UL_TTI_TIMING_OFFSET,
&(pNfapiMsg->nfapi_config.ul_tti_timing_offset),
ppWritePackedMsg,
end,
&pack_uint32_tlv_value);
numTLVs++;
retval &= pack_nr_tlv(NFAPI_NR_NFAPI_UL_DCI_TIMING_OFFSET,
&(pNfapiMsg->nfapi_config.ul_dci_timing_offset),
ppWritePackedMsg,
end,
&pack_uint32_tlv_value);
numTLVs++;
retval &= pack_nr_tlv(NFAPI_NR_NFAPI_TX_DATA_TIMING_OFFSET,
&(pNfapiMsg->nfapi_config.tx_data_timing_offset),
ppWritePackedMsg,
end,
&pack_uint32_tlv_value);
numTLVs++;
// END nFAPI TLVs included in CONFIG.request for IDLE and CONFIGURED states
if (pNfapiMsg->vendor_extension != 0 && config != 0) {
@@ -1419,9 +1471,13 @@ uint8_t unpack_nr_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *
{NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), &unpack_ipv4_address_value},
{NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), &unpack_ipv6_address_value},
{NFAPI_NR_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), &unpack_uint16_tlv_value},
{NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), &unpack_uint8_tlv_value},
{NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), &unpack_uint16_tlv_value},
{NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), &unpack_uint8_tlv_value},
{NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), &unpack_uint8_tlv_value},
{NFAPI_NR_NFAPI_DL_TTI_TIMING_OFFSET, &(pNfapiMsg->nfapi_config.dl_tti_timing_offset), &unpack_uint32_tlv_value},
{NFAPI_NR_NFAPI_UL_TTI_TIMING_OFFSET, &(pNfapiMsg->nfapi_config.ul_tti_timing_offset), &unpack_uint32_tlv_value},
{NFAPI_NR_NFAPI_UL_DCI_TIMING_OFFSET, &(pNfapiMsg->nfapi_config.ul_dci_timing_offset), &unpack_uint32_tlv_value},
{NFAPI_NR_NFAPI_TX_DATA_TIMING_OFFSET, &(pNfapiMsg->nfapi_config.tx_data_timing_offset), &unpack_uint32_tlv_value},
{NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), &unpack_ipv6_address_value},
{NFAPI_NR_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), &unpack_uint16_tlv_value}};

View File

@@ -3700,7 +3700,7 @@ typedef struct
nfapi_ipv6_address_t p7_pnf_address_ipv6;
nfapi_uint16_tlv_t p7_pnf_port;
nfapi_uint8_tlv_t timing_window; //Value: 0 → 30,000 microseconds
nfapi_uint16_tlv_t timing_window; //Value: 0 → 30,000 microseconds
nfapi_uint8_tlv_t timing_info_mode;
nfapi_uint8_tlv_t timing_info_period;

View File

@@ -1181,17 +1181,17 @@ typedef struct {
uint32_t time_since_last_timing_info;
uint32_t dl_tti_jitter;
uint32_t tx_data_request_jitter;
uint32_t tx_data_jitter;
uint32_t ul_tti_jitter;
uint32_t ul_dci_jitter;
int32_t dl_tti_latest_delay;
int32_t tx_data_request_latest_delay;
int32_t tx_data_latest_delay;
int32_t ul_tti_latest_delay;
int32_t ul_dci_latest_delay;
int32_t dl_tti_earliest_arrival;
int32_t tx_data_request_earliest_arrival;
int32_t tx_data_earliest_arrival;
int32_t ul_tti_earliest_arrival;
int32_t ul_dci_earliest_arrival;
nfapi_vendor_extension_tlv_t vendor_extension;

View File

@@ -2445,14 +2445,14 @@ uint8_t pack_nr_timing_info(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end,
return (push32(pNfapiMsg->last_sfn, ppWritePackedMsg, end) && push32(pNfapiMsg->last_slot, ppWritePackedMsg, end)
&& push32(pNfapiMsg->time_since_last_timing_info, ppWritePackedMsg, end)
&& push32(pNfapiMsg->dl_tti_jitter, ppWritePackedMsg, end)
&& push32(pNfapiMsg->tx_data_request_jitter, ppWritePackedMsg, end)
&& push32(pNfapiMsg->tx_data_jitter, ppWritePackedMsg, end)
&& push32(pNfapiMsg->ul_tti_jitter, ppWritePackedMsg, end) && push32(pNfapiMsg->ul_dci_jitter, ppWritePackedMsg, end)
&& pushs32(pNfapiMsg->dl_tti_latest_delay, ppWritePackedMsg, end)
&& pushs32(pNfapiMsg->tx_data_request_latest_delay, ppWritePackedMsg, end)
&& pushs32(pNfapiMsg->tx_data_latest_delay, ppWritePackedMsg, end)
&& pushs32(pNfapiMsg->ul_tti_latest_delay, ppWritePackedMsg, end)
&& pushs32(pNfapiMsg->ul_dci_latest_delay, ppWritePackedMsg, end)
&& pushs32(pNfapiMsg->dl_tti_earliest_arrival, ppWritePackedMsg, end)
&& pushs32(pNfapiMsg->tx_data_request_earliest_arrival, ppWritePackedMsg, end)
&& pushs32(pNfapiMsg->tx_data_earliest_arrival, ppWritePackedMsg, end)
&& pushs32(pNfapiMsg->ul_tti_earliest_arrival, ppWritePackedMsg, end)
&& pushs32(pNfapiMsg->ul_dci_earliest_arrival, ppWritePackedMsg, end)
&& pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config));
@@ -5735,14 +5735,14 @@ static uint8_t unpack_nr_timing_info(uint8_t **ppReadPackedMsg, uint8_t *end, vo
return (pull32(ppReadPackedMsg, &pNfapiMsg->last_sfn, end) && pull32(ppReadPackedMsg, &pNfapiMsg->last_slot, end)
&& pull32(ppReadPackedMsg, &pNfapiMsg->time_since_last_timing_info, end)
&& pull32(ppReadPackedMsg, &pNfapiMsg->dl_tti_jitter, end)
&& pull32(ppReadPackedMsg, &pNfapiMsg->tx_data_request_jitter, end)
&& pull32(ppReadPackedMsg, &pNfapiMsg->tx_data_jitter, end)
&& pull32(ppReadPackedMsg, &pNfapiMsg->ul_tti_jitter, end) && pull32(ppReadPackedMsg, &pNfapiMsg->ul_dci_jitter, end)
&& pulls32(ppReadPackedMsg, &pNfapiMsg->dl_tti_latest_delay, end)
&& pulls32(ppReadPackedMsg, &pNfapiMsg->tx_data_request_latest_delay, end)
&& pulls32(ppReadPackedMsg, &pNfapiMsg->tx_data_latest_delay, end)
&& pulls32(ppReadPackedMsg, &pNfapiMsg->ul_tti_latest_delay, end)
&& pulls32(ppReadPackedMsg, &pNfapiMsg->ul_dci_latest_delay, end)
&& pulls32(ppReadPackedMsg, &pNfapiMsg->dl_tti_earliest_arrival, end)
&& pulls32(ppReadPackedMsg, &pNfapiMsg->tx_data_request_earliest_arrival, end)
&& pulls32(ppReadPackedMsg, &pNfapiMsg->tx_data_earliest_arrival, end)
&& pulls32(ppReadPackedMsg, &pNfapiMsg->ul_tti_earliest_arrival, end)
&& pulls32(ppReadPackedMsg, &pNfapiMsg->ul_dci_earliest_arrival, end)
&& unpack_nr_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension));

View File

@@ -108,19 +108,82 @@ typedef struct pnf_p7_s {
uint8_t timing_info_period_counter;
uint8_t timing_info_aperiodic_send; // 0:false 1:true
uint16_t timing_info_trigger_sfn;
uint16_t timing_info_trigger_slot;
uint32_t timing_info_ms_counter; // number of ms since last timing info
uint32_t timing_info_last_send_time_hr; // TIME_HR when last timing info was sent
uint32_t dl_config_jitter;
uint32_t ul_config_jitter;
uint32_t hi_dci0_jitter;
uint32_t tx_jitter;
//P7 NR
//P7 NR - RFC 3550 jitter calculation state
// Each message type has: jitter value (uint32_t), prev_transit (int64_t), and init flag
uint32_t dl_tti_jitter;
uint32_t ul_tti_jitter;
uint32_t ul_dci_jitter;
uint32_t tx_data_jitter;
// RFC 3550 jitter state: previous transit time (arrival - transmit) in microseconds
int64_t dl_tti_prev_transit_us;
int64_t ul_tti_prev_transit_us;
int64_t ul_dci_prev_transit_us;
int64_t tx_data_prev_transit_us;
// RFC 3550 jitter state (wrap-safe): previous receive time (TIME_HR) and transmit timestamp (µs)
// NOTE: In OAI nFAPI, P7 header transmit_timestamp is derived from SFN/slot and wraps every 10.24s.
// Therefore we compute jitter from deltas (R(i)-R(i-1)) and (S(i)-S(i-1)) with wrap handling.
uint32_t dl_tti_prev_rx_time_hr;
uint32_t ul_tti_prev_rx_time_hr;
uint32_t ul_dci_prev_rx_time_hr;
uint32_t tx_data_prev_rx_time_hr;
uint32_t dl_tti_prev_tx_ts_us;
uint32_t ul_tti_prev_tx_ts_us;
uint32_t ul_dci_prev_tx_ts_us;
uint32_t tx_data_prev_tx_ts_us;
// Smoothed jitter estimate (as double for 1/16 smoothing factor)
double dl_tti_jitter_us;
double ul_tti_jitter_us;
double ul_dci_jitter_us;
double tx_data_jitter_us;
// Init flags for RFC 3550 jitter calculation
uint8_t dl_tti_jitter_init;
uint8_t ul_tti_jitter_init;
uint8_t ul_dci_jitter_init;
uint8_t tx_data_jitter_init;
// Timestamp unwrap state (32-bit to 64-bit conversion)
uint64_t ts_epoch_base;
uint32_t last_ts_32;
// Legacy fields (kept for compatibility)
int32_t dl_tti_prev_transit_time_diff;
int32_t ul_tti_prev_transit_time_diff;
int32_t ul_dci_prev_transit_time_diff;
int32_t tx_data_prev_transit_time_diff;
int32_t dl_tti_latest_delay;
int32_t dl_tti_earliest_arrival;
int32_t ul_tti_latest_delay;
int32_t ul_tti_earliest_arrival;
int32_t ul_dci_latest_delay;
int32_t ul_dci_earliest_arrival;
int32_t tx_data_latest_delay;
int32_t tx_data_earliest_arrival;
// Configuration
uint32_t dl_tti_timing_offset;
uint32_t ul_tti_timing_offset;
uint32_t ul_dci_timing_offset;
uint32_t tx_data_timing_offset;
uint32_t timing_window;
uint32_t timing_info_mode;
uint32_t timing_info_period;
uint32_t tick;
pnf_p7_stats_t stats;
@@ -157,5 +220,42 @@ uint32_t pnf_get_current_time_hr(void);
struct timespec pnf_timespec_add(struct timespec lhs, struct timespec rhs);
void pnf_p7_free(pnf_p7_t* pnf_p7, void* ptr);
void* pnf_p7_malloc(pnf_p7_t* pnf_p7, size_t size);
/*===========================================================================
* RFC 3550 Section 6.4.1 Interarrival Jitter Calculation
*
* The jitter is calculated using the method defined in RFC 3550:
* transit = arrival_time - transmit_timestamp
* d = transit - prev_transit
* jitter = jitter + (|d| - jitter) / 16
*
* For P7 Timing Info, we use:
* - transmit_timestamp: P7 header's Transmit Timestamp (32-bit µs)
* - arrival_time: PHY receive time (µs, from monotonic clock)
*===========================================================================*/
typedef enum {
NFAPI_JITTER_DL_TTI = 0,
NFAPI_JITTER_UL_TTI,
NFAPI_JITTER_UL_DCI,
NFAPI_JITTER_TX_DATA,
NFAPI_JITTER_MAX
} nfapi_jitter_msg_type_t;
// Convert TIME_HR format to microseconds (within the 12-bit second cycle)
uint64_t pnf_timehr_to_us(pnf_p7_t* pnf_p7, uint32_t time_hr);
// Update jitter state using RFC 3550 algorithm
void pnf_update_jitter(pnf_p7_t* pnf_p7,
nfapi_jitter_msg_type_t msg_type,
uint32_t p7_tx_timestamp,
uint32_t recv_time_hr);
// Get jitter value for timing info (uint32_t in µs)
uint32_t pnf_get_jitter(pnf_p7_t* pnf_p7, nfapi_jitter_msg_type_t msg_type);
// Reset jitter state (e.g., on sync reset)
void pnf_reset_jitter(pnf_p7_t* pnf_p7, nfapi_jitter_msg_type_t msg_type);
#endif /* _PNF_P7_H_ */

View File

@@ -626,6 +626,11 @@ typedef struct
nfapi_nr_ul_tti_request_t ul_tti_req;
nfapi_nr_ul_dci_request_t ul_dci_req;
nfapi_nr_tx_data_request_t tx_data_req;
// Receive timestamps for timing calculation (time when packet was received)
uint32_t dl_tti_recv_time_hr;
uint32_t ul_tti_recv_time_hr;
uint32_t ul_dci_recv_time_hr;
uint32_t tx_data_recv_time_hr;
//TODO: check these two later
//nfapi_lbt_dl_config_request_t* lbt_dl_config_req;

View File

@@ -4,6 +4,7 @@
* Copyright 2017 Cisco Systems, Inc.
*/
#define _GNU_SOURCE // for asprintf
#include <sys/select.h>
#include <sys/time.h>
@@ -16,6 +17,7 @@
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <limits.h>
#include "pnf_p7.h"
#include "nr_fapi_p7_utils.h" // for 5G/NR message utils
@@ -24,6 +26,8 @@
#include <SCHED_NR/phy_frame_config_nr.h>
extern int sf_ahead;
// Used by the RFC3550 jitter calculation (defined later in this file)
static inline int64_t timehr_diff_us(uint32_t time_hr_a, uint32_t time_hr_b);
static void add_slot(int mu, uint16_t *frameP, uint16_t *slotP, int offset)
{
@@ -97,6 +101,200 @@ uint32_t pnf_get_current_time_hr(void)
uint32_t time_hr = TIME2TIMEHR(now);
return time_hr;
}
/*===========================================================================
* RFC 3550 Section 6.4.1 Interarrival Jitter Implementation
*
* The interarrival jitter J is defined as the mean deviation of the
* difference D in packet spacing at the receiver compared to the sender.
* It is calculated incrementally:
* D(i) = (R(i) - R(i-1)) - (S(i) - S(i-1)) = (R(i) - S(i)) - (R(i-1) - S(i-1))
* J(i) = J(i-1) + (|D(i)| - J(i-1)) / 16
* where:
* S(i) = transmit timestamp of packet i (from P7 header, in µs)
* R(i) = receive time of packet i (PHY local time, in µs)
* D(i) = difference in transit time between consecutive packets
* J = smoothed jitter estimate
*===========================================================================*/
// Convert TIME_HR format (12-bit sec + 20-bit usec) to microseconds
// Note: TIME_HR seconds wrap every 4096 seconds; wrap-safe differences are handled by timehr_diff_us().
uint64_t pnf_timehr_to_us(pnf_p7_t* pnf_p7, uint32_t time_hr)
{
uint32_t sec = TIMEHR_SEC(time_hr);
uint32_t usec = TIMEHR_USEC(time_hr);
// Convert to 64-bit microseconds (relative, will wrap at 4096 seconds)
return (uint64_t)sec * 1000000ULL + (uint64_t)usec;
}
// In OAI nFAPI, P7 header transmit_timestamp is derived from SFN/slot and wraps every 10.24 seconds.
// We therefore compute S(i)-S(i-1) using wrap-aware arithmetic.
#define NFAPI_P7_TX_TS_WRAP_US 10240000u
static inline int64_t p7_tx_ts_diff_us(uint32_t curr_tx_ts_us, uint32_t prev_tx_ts_us)
{
// Compute minimal signed delta in range [-wrap/2, +wrap/2]
int64_t diff = (int64_t)curr_tx_ts_us - (int64_t)prev_tx_ts_us;
int64_t half = (int64_t)NFAPI_P7_TX_TS_WRAP_US / 2;
if (diff < -half)
diff += (int64_t)NFAPI_P7_TX_TS_WRAP_US;
else if (diff > half)
diff -= (int64_t)NFAPI_P7_TX_TS_WRAP_US;
return diff;
}
// Update jitter for a specific message type using RFC 3550 algorithm
void pnf_update_jitter(pnf_p7_t* pnf_p7,
nfapi_jitter_msg_type_t msg_type,
uint32_t p7_tx_timestamp,
uint32_t recv_time_hr)
{
if (!pnf_p7) return;
// Get pointers to the appropriate state variables based on message type
int64_t *prev_transit_us;
uint32_t *prev_rx_time_hr;
uint32_t *prev_tx_ts_us;
double *jitter_us;
uint8_t *jitter_init;
switch (msg_type) {
case NFAPI_JITTER_DL_TTI:
prev_transit_us = &pnf_p7->dl_tti_prev_transit_us;
prev_rx_time_hr = &pnf_p7->dl_tti_prev_rx_time_hr;
prev_tx_ts_us = &pnf_p7->dl_tti_prev_tx_ts_us;
jitter_us = &pnf_p7->dl_tti_jitter_us;
jitter_init = &pnf_p7->dl_tti_jitter_init;
break;
case NFAPI_JITTER_UL_TTI:
prev_transit_us = &pnf_p7->ul_tti_prev_transit_us;
prev_rx_time_hr = &pnf_p7->ul_tti_prev_rx_time_hr;
prev_tx_ts_us = &pnf_p7->ul_tti_prev_tx_ts_us;
jitter_us = &pnf_p7->ul_tti_jitter_us;
jitter_init = &pnf_p7->ul_tti_jitter_init;
break;
case NFAPI_JITTER_UL_DCI:
prev_transit_us = &pnf_p7->ul_dci_prev_transit_us;
prev_rx_time_hr = &pnf_p7->ul_dci_prev_rx_time_hr;
prev_tx_ts_us = &pnf_p7->ul_dci_prev_tx_ts_us;
jitter_us = &pnf_p7->ul_dci_jitter_us;
jitter_init = &pnf_p7->ul_dci_jitter_init;
break;
case NFAPI_JITTER_TX_DATA:
prev_transit_us = &pnf_p7->tx_data_prev_transit_us;
prev_rx_time_hr = &pnf_p7->tx_data_prev_rx_time_hr;
prev_tx_ts_us = &pnf_p7->tx_data_prev_tx_ts_us;
jitter_us = &pnf_p7->tx_data_jitter_us;
jitter_init = &pnf_p7->tx_data_jitter_init;
break;
default:
return;
}
// First packet - initialize state
if (!(*jitter_init)) {
*prev_rx_time_hr = recv_time_hr;
*prev_tx_ts_us = p7_tx_timestamp;
*prev_transit_us = 0;
*jitter_us = 0.0;
*jitter_init = 1;
return;
}
// RFC3550 uses packet spacing deltas:
// D(i) = (R(i)-R(i-1)) - (S(i)-S(i-1))
// Here:
// R is local receive time (TIME_HR)
// S is P7 transmit_timestamp (µs) which wraps every 10.24s
int64_t delta_r_us = timehr_diff_us(recv_time_hr, *prev_rx_time_hr);
int64_t delta_s_us = p7_tx_ts_diff_us(p7_tx_timestamp, *prev_tx_ts_us);
// Update history immediately (even if we decide to re-init)
*prev_rx_time_hr = recv_time_hr;
*prev_tx_ts_us = p7_tx_timestamp;
// If timestamps go backwards (re-ordering or discontinuity), re-initialize.
// This prevents spuriously treating small backwards steps as a wrap-around.
if (delta_r_us < 0 || delta_s_us < 0) {
*prev_transit_us = 0;
*jitter_us = 0.0;
return;
}
int64_t d = delta_r_us - delta_s_us;
if (d < 0) d = -d;
*jitter_us += ((double)d - *jitter_us) / 16.0;
}
// Get jitter value as uint32_t for Timing Info message
uint32_t pnf_get_jitter(pnf_p7_t* pnf_p7, nfapi_jitter_msg_type_t msg_type)
{
if (!pnf_p7) return 0;
double jitter;
uint8_t init;
switch (msg_type) {
case NFAPI_JITTER_DL_TTI:
jitter = pnf_p7->dl_tti_jitter_us;
init = pnf_p7->dl_tti_jitter_init;
break;
case NFAPI_JITTER_UL_TTI:
jitter = pnf_p7->ul_tti_jitter_us;
init = pnf_p7->ul_tti_jitter_init;
break;
case NFAPI_JITTER_UL_DCI:
jitter = pnf_p7->ul_dci_jitter_us;
init = pnf_p7->ul_dci_jitter_init;
break;
case NFAPI_JITTER_TX_DATA:
jitter = pnf_p7->tx_data_jitter_us;
init = pnf_p7->tx_data_jitter_init;
break;
default:
return 0;
}
if (!init) return 0;
if (jitter < 0) jitter = 0;
if (jitter > 4294967295.0) return 0xFFFFFFFFu;
return (uint32_t)(jitter + 0.5); // Round to nearest integer
}
// Reset jitter state for a specific message type
void pnf_reset_jitter(pnf_p7_t* pnf_p7, nfapi_jitter_msg_type_t msg_type)
{
if (!pnf_p7) return;
switch (msg_type) {
case NFAPI_JITTER_DL_TTI:
pnf_p7->dl_tti_jitter_init = 0;
pnf_p7->dl_tti_jitter_us = 0.0;
pnf_p7->dl_tti_prev_transit_us = 0;
pnf_p7->dl_tti_prev_rx_time_hr = 0;
pnf_p7->dl_tti_prev_tx_ts_us = 0;
break;
case NFAPI_JITTER_UL_TTI:
pnf_p7->ul_tti_jitter_init = 0;
pnf_p7->ul_tti_jitter_us = 0.0;
pnf_p7->ul_tti_prev_transit_us = 0;
pnf_p7->ul_tti_prev_rx_time_hr = 0;
pnf_p7->ul_tti_prev_tx_ts_us = 0;
break;
case NFAPI_JITTER_UL_DCI:
pnf_p7->ul_dci_jitter_init = 0;
pnf_p7->ul_dci_jitter_us = 0.0;
pnf_p7->ul_dci_prev_transit_us = 0;
pnf_p7->ul_dci_prev_rx_time_hr = 0;
pnf_p7->ul_dci_prev_tx_ts_us = 0;
break;
case NFAPI_JITTER_TX_DATA:
pnf_p7->tx_data_jitter_init = 0;
pnf_p7->tx_data_jitter_us = 0.0;
pnf_p7->tx_data_prev_transit_us = 0;
pnf_p7->tx_data_prev_rx_time_hr = 0;
pnf_p7->tx_data_prev_tx_ts_us = 0;
break;
default:
break;
}
}
void* pnf_p7_malloc(pnf_p7_t* pnf_p7, size_t size)
{
@@ -392,26 +590,33 @@ void pnf_p7_rx_reassembly_queue_remove_old_msgs(pnf_p7_t* pnf_p7, pnf_p7_rx_reas
}
/*! Compute signed difference between two TIMEHR timestamps in microseconds.
* Handles 12-bit second wrap-around (every 4096 seconds) correctly
* for differences up to ~2048 seconds.
*/
static inline int64_t timehr_diff_us(uint32_t time_hr_a, uint32_t time_hr_b)
{
// Extract seconds and microseconds
int32_t sec_a = TIMEHR_SEC(time_hr_a);
int32_t sec_b = TIMEHR_SEC(time_hr_b);
int32_t usec_a = TIMEHR_USEC(time_hr_a);
int32_t usec_b = TIMEHR_USEC(time_hr_b);
// Handle 12-bit second wrap-around
// sec_a - sec_b should be in range [-2048, 2047] for valid comparisons
int32_t sec_diff = sec_a - sec_b;
if (sec_diff > 2048) sec_diff -= 4096; // sec_a wrapped, sec_b didn't
if (sec_diff < -2048) sec_diff += 4096; // sec_b wrapped, sec_a didn't
return (int64_t)sec_diff * 1000000 + (usec_a - usec_b);
}
static uint32_t get_slot_time(uint32_t now_hr, uint32_t slot_start_hr)
{
if(now_hr < slot_start_hr)
{
//NFAPI_TRACE(NFAPI_TRACE_INFO, "now is earlier than start of subframe now_hr:%u sf_start_hr:%u\n", now_hr, sf_start_hr);
return 0;
}
else
{
uint32_t now_us = TIMEHR_USEC(now_hr);
uint32_t slot_start_us = TIMEHR_USEC(slot_start_hr);
// if the us have wrapped adjust for it
if(now_hr < slot_start_us)
{
now_us += 500000;
}
return now_us - slot_start_us;
}
// Use proper signed difference to handle wrap-around
int64_t diff_us = timehr_diff_us(now_hr, slot_start_hr);
if (diff_us < 0) return 0;
return (uint32_t)diff_us;
}
static uint32_t get_sf_time(uint32_t now_hr, uint32_t sf_start_hr)
@@ -437,6 +642,64 @@ static uint32_t get_sf_time(uint32_t now_hr, uint32_t sf_start_hr)
}
static inline int32_t calc_slot_diff(pnf_p7_t* pnf_p7, uint16_t msg_sfn, uint16_t msg_slot)
{
int32_t diff = NFAPI_SFNSLOT2DEC(pnf_p7->mu, msg_sfn, msg_slot)
- NFAPI_SFNSLOT2DEC(pnf_p7->mu, pnf_p7->sfn, pnf_p7->slot);
int32_t half_max = NFAPI_MAX_SFNSLOTDEC(pnf_p7->mu) / 2;
if (diff < -half_max) diff += 2 * half_max;
if (diff > half_max) diff -= 2 * half_max;
return diff;
}
// Forward declaration
void pnf_nr_pack_and_send_timing_info(pnf_p7_t* pnf_p7);
static bool check_nr_p7_timing(pnf_p7_t* pnf_p7, uint16_t msg_sfn, uint16_t msg_slot,
const char* name, uint32_t recv_time_hr,
uint32_t timing_offset, int32_t* latest_delay, int32_t* earliest_arrival)
{
// Calculate difference in slots (handling wrap-around)
int32_t diff_slots = calc_slot_diff(pnf_p7, msg_sfn, msg_slot);
int64_t slot_len_us = 10000 / NFAPI_SLOTNUM(pnf_p7->mu);
// Calculate margin: Time remaining until deadline
int64_t time_since_slot_start = timehr_diff_us(recv_time_hr, pnf_p7->slot_start_time_hr);
int64_t delay_to_msg_slot = diff_slots * slot_len_us;
int64_t margin = delay_to_msg_slot - time_since_slot_start - timing_offset;
// Offset = RecvTime - (TargetTime - TimingOffset) = -Margin
// Positive Value: Later than acceptable (LATE)
// Negative Value: Earlier than acceptable (EARLY)
int64_t offset = -margin;
// Update Latest Delay (Max Positive Offset)
if (offset > *latest_delay) {
*latest_delay = (int32_t)offset;
}
// Update Earliest Arrival (Min Negative Offset)
if (offset < *earliest_arrival) {
*earliest_arrival = (int32_t)offset;
}
if (margin < 0 || margin > (int64_t)pnf_p7->timing_window) {
if (margin < 0) {
NFAPI_TRACE(NFAPI_TRACE_WARN, "%s [%d.%d] TOO LATE by %ld us\n", name, msg_sfn, msg_slot, (long)(-margin));
} else {
NFAPI_TRACE(NFAPI_TRACE_WARN, "%s too early by %ld us (window:%u)\n",
name, (long)(margin - pnf_p7->timing_window), pnf_p7->timing_window);
}
if (pnf_p7->_public.timing_info_mode_aperiodic) {
pnf_p7->timing_info_aperiodic_send = 1;
pnf_p7->timing_info_trigger_sfn = msg_sfn;
pnf_p7->timing_info_trigger_slot = msg_slot;
}
return false;
}
return true; // Packet is within window
}
int pnf_p7_send_message(pnf_p7_t* pnf_p7, uint8_t* msg, uint32_t len)
{
@@ -597,28 +860,53 @@ void pnf_nr_pack_and_send_timing_info(pnf_p7_t* pnf_p7)
timing_info.header.message_id = NFAPI_TIMING_INFO;
timing_info.header.phy_id = pnf_p7->_public.phy_id;
timing_info.last_sfn = pnf_p7->sfn;
timing_info.last_slot = pnf_p7->slot;
timing_info.time_since_last_timing_info = pnf_p7->timing_info_ms_counter;
uint32_t last_slot_dec = NFAPI_SFNSLOT2DEC(pnf_p7->mu, pnf_p7->sfn, pnf_p7->slot);
uint32_t max_slots = NFAPI_MAX_SFNSLOTDEC(pnf_p7->mu);
last_slot_dec = (last_slot_dec + max_slots - 1) % max_slots;
timing_info.last_sfn = NFAPI_SFNSLOTDEC2SFN(pnf_p7->mu, last_slot_dec);
timing_info.last_slot = NFAPI_SFNSLOTDEC2SLOT(pnf_p7->mu, last_slot_dec);
// Calculate actual elapsed time since last timing info using timestamps
uint32_t now_time_hr = pnf_get_current_time_hr();
int64_t elapsed_us = timehr_diff_us(now_time_hr, pnf_p7->timing_info_last_send_time_hr);
if (elapsed_us < 0) elapsed_us = 0; // Handle first call or wrap-around edge case
timing_info.time_since_last_timing_info = (uint32_t)(elapsed_us / 1000); // Convert to ms
timing_info.dl_tti_jitter = pnf_p7->dl_tti_jitter;
timing_info.tx_data_request_jitter = pnf_p7->tx_data_jitter;
timing_info.ul_tti_jitter = pnf_p7->ul_tti_jitter;
timing_info.ul_dci_jitter = pnf_p7->ul_dci_jitter;
// Use RFC 3550 calculated jitter values (in microseconds)
timing_info.dl_tti_jitter = pnf_get_jitter(pnf_p7, NFAPI_JITTER_DL_TTI);
timing_info.tx_data_jitter = pnf_get_jitter(pnf_p7, NFAPI_JITTER_TX_DATA);
timing_info.ul_tti_jitter = pnf_get_jitter(pnf_p7, NFAPI_JITTER_UL_TTI);
timing_info.ul_dci_jitter = pnf_get_jitter(pnf_p7, NFAPI_JITTER_UL_DCI);
timing_info.dl_tti_latest_delay = 0;
timing_info.tx_data_request_latest_delay = 0;
timing_info.ul_tti_latest_delay = 0;
timing_info.ul_dci_latest_delay = 0;
// If latest_delay is still INT32_MIN, no packets of that type were received; report 0
// If earliest_arrival is still INT32_MAX, no packets of that type were received; report 0
timing_info.dl_tti_latest_delay = (pnf_p7->dl_tti_latest_delay == INT32_MIN) ? 0 : pnf_p7->dl_tti_latest_delay;
timing_info.tx_data_latest_delay = (pnf_p7->tx_data_latest_delay == INT32_MIN) ? 0 : pnf_p7->tx_data_latest_delay;
timing_info.ul_tti_latest_delay = (pnf_p7->ul_tti_latest_delay == INT32_MIN) ? 0 : pnf_p7->ul_tti_latest_delay;
timing_info.ul_dci_latest_delay = (pnf_p7->ul_dci_latest_delay == INT32_MIN) ? 0 : pnf_p7->ul_dci_latest_delay;
timing_info.dl_tti_earliest_arrival = 0;
timing_info.tx_data_request_earliest_arrival = 0;
timing_info.ul_tti_earliest_arrival = 0;
timing_info.ul_dci_earliest_arrival = 0;
AssertFatal(pnf_p7->_public.send_p7_msg, "The function pointer to pack and send P7 messages must be set");
pnf_p7->_public.send_p7_msg(pnf_p7, &(timing_info.header), sizeof(timing_info));
timing_info.dl_tti_earliest_arrival = (pnf_p7->dl_tti_earliest_arrival == INT32_MAX) ? 0 : pnf_p7->dl_tti_earliest_arrival;
timing_info.tx_data_earliest_arrival = (pnf_p7->tx_data_earliest_arrival == INT32_MAX) ? 0 : pnf_p7->tx_data_earliest_arrival;
timing_info.ul_tti_earliest_arrival = (pnf_p7->ul_tti_earliest_arrival == INT32_MAX) ? 0 : pnf_p7->ul_tti_earliest_arrival;
timing_info.ul_dci_earliest_arrival = (pnf_p7->ul_dci_earliest_arrival == INT32_MAX) ? 0 : pnf_p7->ul_dci_earliest_arrival;
AssertFatal(pnf_p7->_public.send_p7_msg, "The function pointer to pack and send P7 messages must be set");
pnf_p7->_public.send_p7_msg(pnf_p7, &(timing_info.header), sizeof(timing_info));
pnf_p7->timing_info_ms_counter = 0;
// Update last send time for next elapsed time calculation
pnf_p7->timing_info_last_send_time_hr = now_time_hr;
pnf_p7->timing_info_aperiodic_send = 0;
// Reset latest_delay and earliest_arrival for next timing info period
// Note: jitter state is NOT reset - it's a running average per RFC 3550
// Per SCF 225 Table 4-3: latest_delay can be negative (early), so use INT32_MIN as sentinel
// earliest_arrival uses INT32_MAX as sentinel
pnf_p7->dl_tti_latest_delay = INT32_MIN;
pnf_p7->ul_tti_latest_delay = INT32_MIN;
pnf_p7->ul_dci_latest_delay = INT32_MIN;
pnf_p7->tx_data_latest_delay = INT32_MIN;
pnf_p7->dl_tti_earliest_arrival = INT32_MAX;
pnf_p7->ul_tti_earliest_arrival = INT32_MAX;
pnf_p7->ul_dci_earliest_arrival = INT32_MAX;
pnf_p7->tx_data_earliest_arrival = INT32_MAX;
}
void send_dummy_subframe(pnf_p7_t* pnf_p7, uint16_t sfn_sf)
@@ -692,6 +980,16 @@ int nr_pnf_p7_get_msgs(pnf_p7_t* pnf_p7,
if (pnf_p7->_public.slot_buffer_size != 0) // for now value is same as sf_buffer_size
{
// apply the shift to the incoming sfn_sf
// send the periodic timing info if configured
// This is done at the START of the slot processing to cover the previous slot completion
if (pnf_p7->_public.timing_info_mode_periodic && (++pnf_p7->timing_info_period_counter) >= pnf_p7->_public.timing_info_period) {
pnf_nr_pack_and_send_timing_info(pnf_p7);
pnf_p7->timing_info_period_counter = 0;
} else if (pnf_p7->_public.timing_info_mode_aperiodic && pnf_p7->timing_info_aperiodic_send) {
pnf_nr_pack_and_send_timing_info(pnf_p7);
}
if (pnf_p7->slot_shift != 0) // see in vnf_build_send_dl_node_sync
{
uint16_t shifted_slot = slot + pnf_p7->slot_shift;
@@ -720,7 +1018,7 @@ int nr_pnf_p7_get_msgs(pnf_p7_t* pnf_p7,
ret_dl_tti->dl_tti_request_body.nPDUs = 0;
nfapi_nr_dl_tti_request_t* dl_tti_req = &tx_slot_buffer->dl_tti_req;
if (dl_tti_req->SFN == sfn && dl_tti_req->Slot == slot) {
copy_dl_tti_request(dl_tti_req, ret_dl_tti);
copy_dl_tti_request(dl_tti_req, ret_dl_tti);
tx_slot_buffer->dl_tti_req.SFN = -1;
tx_slot_buffer->dl_tti_req.Slot = -1;
}
@@ -730,7 +1028,7 @@ int nr_pnf_p7_get_msgs(pnf_p7_t* pnf_p7,
ret_tx_data->Number_of_PDUs = 0;
nfapi_nr_tx_data_request_t* txd = &tx_slot_buffer->tx_data_req;
if (txd->SFN == sfn && txd->Slot == slot) {
copy_tx_data_request(txd, ret_tx_data);
copy_tx_data_request(txd, ret_tx_data);
tx_slot_buffer->tx_data_req.SFN = -1;
tx_slot_buffer->tx_data_req.Slot = -1;
}
@@ -739,7 +1037,7 @@ int nr_pnf_p7_get_msgs(pnf_p7_t* pnf_p7,
ret_ul_tti->Slot = slot;
ret_ul_tti->n_pdus = 0;
if (tx_slot_buffer->ul_tti_req.SFN == sfn && tx_slot_buffer->ul_tti_req.Slot == slot) {
copy_ul_tti_request(&tx_slot_buffer->ul_tti_req, ret_ul_tti);
copy_ul_tti_request(&tx_slot_buffer->ul_tti_req, ret_ul_tti);
tx_slot_buffer->ul_tti_req.SFN = -1;
tx_slot_buffer->ul_tti_req.Slot = -1;
}
@@ -748,23 +1046,10 @@ int nr_pnf_p7_get_msgs(pnf_p7_t* pnf_p7,
ret_ul_dci->Slot = slot;
ret_ul_dci->numPdus = 0;
if (tx_slot_buffer->ul_dci_req.SFN == sfn && tx_slot_buffer->ul_dci_req.Slot == slot) {
copy_ul_dci_request(&tx_slot_buffer->ul_dci_req, ret_ul_dci);
copy_ul_dci_request(&tx_slot_buffer->ul_dci_req, ret_ul_dci);
tx_slot_buffer->ul_dci_req.SFN = -1;
tx_slot_buffer->ul_dci_req.Slot = -1;
}
// send the periodic timing info if configured
if (pnf_p7->_public.timing_info_mode_periodic && (pnf_p7->timing_info_period_counter++) == pnf_p7->_public.timing_info_period) {
pnf_nr_pack_and_send_timing_info(pnf_p7);
pnf_p7->timing_info_period_counter = 0;
} else if (pnf_p7->_public.timing_info_mode_aperiodic && pnf_p7->timing_info_aperiodic_send) {
pnf_nr_pack_and_send_timing_info(pnf_p7);
pnf_p7->timing_info_aperiodic_send = 0;
} else {
pnf_p7->timing_info_ms_counter++;
}
}
if (sfn % 128 == 0 && slot == 0) {
@@ -1079,17 +1364,21 @@ int pnf_p7_subframe_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn_sf)
return 0;
}
bool is_nr_p7_request_in_window(const uint16_t sfn, const uint16_t slot, const char* name, const pnf_p7_t* phy)
bool is_nr_p7_request_in_buffer_size(const uint16_t sfn, const uint16_t slot, const char* name, const pnf_p7_t* phy)
{
const uint32_t recv = NFAPI_SFNSLOT2DEC(phy->mu, sfn, slot); // unpack sfn/slot
const uint32_t curr = NFAPI_SFNSLOT2DEC(phy->mu, phy->sfn, phy->slot);
const uint8_t timing_window = phy->_public.slot_buffer_size; // TODO check
const uint16_t timing_window = phy->_public.slot_buffer_size; // TODO check
uint32_t diff = curr < recv ? recv - curr : curr - recv;
if (diff > NFAPI_MAX_SFNSLOTDEC(phy->mu) / 2)
diff = NFAPI_MAX_SFNSLOTDEC(phy->mu) - diff;
if (diff > timing_window) {
NFAPI_TRACE(NFAPI_TRACE_WARN, "[%d] %s is out of window %d (delta:%d) [max:%d]\n", curr, name, recv, diff, timing_window);
return false;
NFAPI_TRACE(NFAPI_TRACE_WARN, "%s is out of buffer window recv: %d.%d curr: %d.%d (delta:%d) [max:%d]\n", name,
sfn, slot,
phy->sfn, phy->slot,
diff * (curr < recv ? 1 : -1),
timing_window);
return false;
}
return true;
}
@@ -1174,19 +1463,39 @@ uint8_t is_p7_request_in_window(uint16_t sfnsf, const char* name, pnf_p7_t* phy)
// P7 messages
void pnf_handle_dl_tti_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7)
{
// NFAPI_TRACE(NFAPI_TRACE_INFO, "DL_CONFIG.req Received\n");
// Record receive time immediately when packet arrives
uint32_t recv_time_hr = pnf_get_current_time_hr();
uint16_t frame, slot;
if (peek_nr_nfapi_p7_sfn_slot(pRecvMsg, recvMsgLen, &frame, &slot)) {
nfapi_nr_p7_message_header_t header;
if (!nfapi_nr_p7_message_header_unpack(pRecvMsg, recvMsgLen, &header, sizeof(header), &pnf_p7->_public.codec_config)) {
NFAPI_TRACE(NFAPI_TRACE_ERROR, "Failed to unpack header in %s\n", __FUNCTION__);
return;
}
if (pthread_mutex_lock(&(pnf_p7->mutex)) != 0) {
NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to lock mutex\n");
return;
}
if (check_nr_nfapi_p7_slot_type(frame, slot, "DL_TTI.request", NR_DOWNLINK_SLOT)
&& is_nr_p7_request_in_window(frame, slot, "dl_tti_request", pnf_p7)) {
// Update RFC 3550 jitter calculation for DL_TTI
pnf_update_jitter(pnf_p7, NFAPI_JITTER_DL_TTI, header.transmit_timestamp, recv_time_hr);
// Combined check: slot type, buffer size, and timing (not late)
// If any check fails, packet is dropped (not processed)
// Run checks independently to prevent short-circuiting
// We MUST run check_nr_p7_timing to update delay/early stats and trigger aperiodic info
bool type_ok = check_nr_nfapi_p7_slot_type(frame, slot, "DL_TTI.request", NR_DOWNLINK_SLOT);
bool buffer_ok = is_nr_p7_request_in_buffer_size(frame, slot, "dl_tti_request", pnf_p7);
bool timing_ok = check_nr_p7_timing(pnf_p7, frame, slot, "dl_tti_request",
recv_time_hr, pnf_p7->dl_tti_timing_offset,
&pnf_p7->dl_tti_latest_delay,
&pnf_p7->dl_tti_earliest_arrival);
if (type_ok && buffer_ok && timing_ok) {
// Packet arrived on time - store in buffer
uint32_t sfn_slot_dec = NFAPI_SFNSLOT2DEC(pnf_p7->mu, frame, slot);
uint8_t buffer_index = sfn_slot_dec % NFAPI_SLOTNUM(pnf_p7->mu);
pnf_p7->slot_buffer[buffer_index].sfn = frame;
pnf_p7->slot_buffer[buffer_index].slot = slot;
pnf_p7->slot_buffer[buffer_index].dl_tti_recv_time_hr = recv_time_hr;
nfapi_nr_dl_tti_request_t *req = &pnf_p7->slot_buffer[buffer_index].dl_tti_req;
pnf_p7->nr_stats.dl_tti.ontime++;
@@ -1200,10 +1509,6 @@ void pnf_handle_dl_tti_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7)
const bool result = pnf_p7->_public.unpack_func(pRecvMsg, recvMsgLen, req, sizeof(*req), &(pnf_p7->_public.codec_config));
if (!result)
NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to unpack request\n");
} else {
if (pnf_p7->_public.timing_info_mode_aperiodic)
pnf_p7->timing_info_aperiodic_send = 1;
pnf_p7->nr_stats.dl_tti.late++;
}
if (pthread_mutex_unlock(&(pnf_p7->mutex)) != 0) {
@@ -1307,19 +1612,34 @@ void pnf_handle_dl_config_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_
void pnf_handle_ul_tti_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7)
{
uint32_t recv_time_hr = pnf_get_current_time_hr();
uint16_t frame, slot;
if (peek_nr_nfapi_p7_sfn_slot(pRecvMsg, recvMsgLen, &frame, &slot)) {
nfapi_nr_p7_message_header_t header;
if (!nfapi_nr_p7_message_header_unpack(pRecvMsg, recvMsgLen, &header, sizeof(header), &pnf_p7->_public.codec_config)) {
NFAPI_TRACE(NFAPI_TRACE_ERROR, "Failed to unpack header in %s\n", __FUNCTION__);
return;
}
if (pthread_mutex_lock(&(pnf_p7->mutex)) != 0) {
NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to lock mutex\n");
return;
}
pnf_update_jitter(pnf_p7, NFAPI_JITTER_UL_TTI, header.transmit_timestamp, recv_time_hr);
if (check_nr_nfapi_p7_slot_type(frame, slot, "UL_TTI.request", NR_UPLINK_SLOT)
&& is_nr_p7_request_in_window(frame, slot, "ul_tti_request", pnf_p7)) {
// Run checks independently to prevent short-circuiting
bool type_ok = check_nr_nfapi_p7_slot_type(frame, slot, "UL_TTI.request", NR_UPLINK_SLOT);
bool buffer_ok = is_nr_p7_request_in_buffer_size(frame, slot, "ul_tti_request", pnf_p7);
bool timing_ok = check_nr_p7_timing(pnf_p7, frame, slot, "ul_tti_request",
recv_time_hr, pnf_p7->ul_tti_timing_offset,
&pnf_p7->ul_tti_latest_delay,
&pnf_p7->ul_tti_earliest_arrival);
if (type_ok && buffer_ok && timing_ok) {
uint32_t sfn_slot_dec = NFAPI_SFNSLOT2DEC(pnf_p7->mu, frame, slot);
uint8_t buffer_index = sfn_slot_dec % NFAPI_SLOTNUM(pnf_p7->mu);
pnf_p7->slot_buffer[buffer_index].sfn = frame;
pnf_p7->slot_buffer[buffer_index].slot = slot;
pnf_p7->slot_buffer[buffer_index].ul_tti_recv_time_hr = recv_time_hr;
nfapi_nr_ul_tti_request_t* req = &pnf_p7->slot_buffer[buffer_index].ul_tti_req;
pnf_p7->nr_stats.ul_tti.ontime++;
@@ -1333,14 +1653,6 @@ void pnf_handle_ul_tti_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7)
const bool result = pnf_p7->_public.unpack_func(pRecvMsg, recvMsgLen, req, sizeof(*req), &(pnf_p7->_public.codec_config));
if (!result)
NFAPI_TRACE(NFAPI_TRACE_ERROR, "failed to unpack UL_TTI.request\n");
} else {
NFAPI_TRACE(NFAPI_TRACE_NOTE,
"[%d.%d] NOT storing ul_tti_req OUTSIDE OF TRANSMIT BUFFER WINDOW SFN/SLOT %d.%d\n",
pnf_p7->sfn, pnf_p7->slot,
frame, slot);
if (pnf_p7->_public.timing_info_mode_aperiodic)
pnf_p7->timing_info_aperiodic_send = 1;
pnf_p7->nr_stats.ul_tti.late++;
}
@@ -1427,18 +1739,33 @@ void pnf_handle_ul_config_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_
void pnf_handle_ul_dci_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7)
{
uint32_t recv_time_hr = pnf_get_current_time_hr();
uint16_t frame, slot;
if (peek_nr_nfapi_p7_sfn_slot(pRecvMsg, recvMsgLen, &frame, &slot)) {
nfapi_nr_p7_message_header_t header;
if (!nfapi_nr_p7_message_header_unpack(pRecvMsg, recvMsgLen, &header, sizeof(header), &pnf_p7->_public.codec_config)) {
NFAPI_TRACE(NFAPI_TRACE_ERROR, "Failed to unpack header in %s\n", __FUNCTION__);
return;
}
if (pthread_mutex_lock(&(pnf_p7->mutex)) != 0) {
NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to lock mutex\n");
return;
}
if (check_nr_nfapi_p7_slot_type(frame, slot, "UL_DCI.request", NR_DOWNLINK_SLOT)
&& is_nr_p7_request_in_window(frame, slot, "ul_dci_request", pnf_p7)) {
pnf_update_jitter(pnf_p7, NFAPI_JITTER_UL_DCI, header.transmit_timestamp, recv_time_hr);
// Run checks independently to prevent short-circuiting
bool type_ok = check_nr_nfapi_p7_slot_type(frame, slot, "UL_DCI.request", NR_DOWNLINK_SLOT);
bool buffer_ok = is_nr_p7_request_in_buffer_size(frame, slot, "ul_dci_request", pnf_p7);
bool timing_ok = check_nr_p7_timing(pnf_p7, frame, slot, "ul_dci_request",
recv_time_hr, pnf_p7->ul_dci_timing_offset,
&pnf_p7->ul_dci_latest_delay,
&pnf_p7->ul_dci_earliest_arrival);
if (type_ok && buffer_ok && timing_ok) {
uint32_t sfn_slot_dec = NFAPI_SFNSLOT2DEC(pnf_p7->mu, frame, slot);
uint8_t buffer_index = sfn_slot_dec % NFAPI_SLOTNUM(pnf_p7->mu);
pnf_p7->slot_buffer[buffer_index].sfn = frame;
pnf_p7->slot_buffer[buffer_index].slot = slot;
pnf_p7->slot_buffer[buffer_index].ul_dci_recv_time_hr = recv_time_hr;
nfapi_nr_ul_dci_request_t *req = &pnf_p7->slot_buffer[buffer_index].ul_dci_req;
pnf_p7->nr_stats.ul_dci.ontime++;
@@ -1452,11 +1779,6 @@ void pnf_handle_ul_dci_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7)
const bool result = pnf_p7->_public.unpack_func(pRecvMsg, recvMsgLen, req, sizeof(*req), &(pnf_p7->_public.codec_config));
if (!result)
NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to unpack request\n");
} else {
if (pnf_p7->_public.timing_info_mode_aperiodic) {
pnf_p7->timing_info_aperiodic_send = 1;
}
pnf_p7->nr_stats.ul_dci.late++;
}
@@ -1465,7 +1787,7 @@ void pnf_handle_ul_dci_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7)
return;
}
} else {
NFAPI_TRACE(NFAPI_TRACE_ERROR, "Failed to unpack UL DCI req\n");
NFAPI_TRACE(NFAPI_TRACE_ERROR, "Failed to unpack ul_dci_req\n");
}
}
@@ -1537,21 +1859,42 @@ void pnf_handle_hi_dci0_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7
deallocate_nfapi_hi_dci0_request(req, pnf_p7);
}
}
struct timespec time_now(void)
{
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return t;
}
void pnf_handle_tx_data_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7)
{
uint32_t recv_time_hr = pnf_get_current_time_hr();
uint16_t frame, slot;
if (peek_nr_nfapi_p7_sfn_slot(pRecvMsg, recvMsgLen, &frame, &slot)) {
nfapi_nr_p7_message_header_t header;
if (!nfapi_nr_p7_message_header_unpack(pRecvMsg, recvMsgLen, &header, sizeof(header), &pnf_p7->_public.codec_config)) {
NFAPI_TRACE(NFAPI_TRACE_ERROR, "Failed to unpack header in %s\n", __FUNCTION__);
return;
}
if (pthread_mutex_lock(&(pnf_p7->mutex)) != 0) {
NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to lock mutex\n");
return;
}
if (check_nr_nfapi_p7_slot_type(frame, slot, "TX_DATA.REQUEST", NR_DOWNLINK_SLOT)
&& is_nr_p7_request_in_window(frame, slot, "tx_request", pnf_p7)) {
pnf_update_jitter(pnf_p7, NFAPI_JITTER_TX_DATA, header.transmit_timestamp, recv_time_hr);
// Run checks independently to prevent short-circuiting
bool type_ok = check_nr_nfapi_p7_slot_type(frame, slot, "TX_DATA.REQUEST", NR_DOWNLINK_SLOT);
bool buffer_ok = is_nr_p7_request_in_buffer_size(frame, slot, "tx_data_request", pnf_p7);
bool timing_ok = check_nr_p7_timing(pnf_p7, frame, slot, "tx_data_request",
recv_time_hr, pnf_p7->tx_data_timing_offset,
&pnf_p7->tx_data_latest_delay,
&pnf_p7->tx_data_earliest_arrival);
if (type_ok && buffer_ok && timing_ok) {
uint32_t sfn_slot_dec = NFAPI_SFNSLOT2DEC(pnf_p7->mu, frame, slot);
uint8_t buffer_index = sfn_slot_dec % NFAPI_SLOTNUM(pnf_p7->mu); // TODO where is buffer length?
uint8_t buffer_index = sfn_slot_dec % NFAPI_SLOTNUM(pnf_p7->mu);
pnf_p7->slot_buffer[buffer_index].sfn = frame;
pnf_p7->slot_buffer[buffer_index].slot = slot;
pnf_p7->slot_buffer[buffer_index].tx_data_recv_time_hr = recv_time_hr;
nfapi_nr_tx_data_request_t *req = &pnf_p7->slot_buffer[buffer_index].tx_data_req;
pnf_p7->nr_stats.tx_data.ontime++;
@@ -1569,16 +1912,6 @@ void pnf_handle_tx_data_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7
} else {
NFAPI_TRACE(NFAPI_TRACE_ERROR, "failed to unpack TX_data.request\n");
}
} else {
NFAPI_TRACE(NFAPI_TRACE_INFO,
"TX_DATA_REQUEST Request is outside of window REQ:SFN_SLOT:%d.%d CURR:SFN_SLOT:%d.%d\n",
frame, slot,
pnf_p7->sfn, pnf_p7->slot);
if (pnf_p7->_public.timing_info_mode_aperiodic) {
pnf_p7->timing_info_aperiodic_send = 1;
}
pnf_p7->nr_stats.tx_data.late++;
}

View File

@@ -17,6 +17,7 @@ nfapi_pnf_p7_config_t* nfapi_pnf_p7_config_create()
if (_this == NULL || rc != 0)
return 0;
memset(_this, 0, sizeof(pnf_p7_t));
// set the default parameters
_this->_public.segment_size = 65000; // UDP max packet size is 65535
@@ -27,6 +28,10 @@ nfapi_pnf_p7_config_t* nfapi_pnf_p7_config_create()
_this->_public.timing_info_period = 32;
_this->_public.timing_info_mode_aperiodic = 1;
// By default enable aperiodic timing info send flag (for VNF tick sync)
_this->timing_info_aperiodic_send = 1;
// Initialize last send time for accurate elapsed time calculation
_this->timing_info_last_send_time_hr = pnf_get_current_time_hr();
_this->_public.checksum_enabled = 1;
_this->_public.malloc = &malloc;

View File

@@ -9,10 +9,17 @@
#define _VNF_P7_H_
#include "nfapi_vnf_interface.h"
#include <stdatomic.h>
#define TIMEHR_SEC(_time_hr) ((uint32_t)(_time_hr) >> 20)
#define TIMEHR_USEC(_time_hr) ((uint32_t)(_time_hr) & 0xFFFFF)
#define TIME2TIMEHR(_time) (((uint32_t)(_time.tv_sec) & 0xFFF) << 20 | ((uint32_t)(_time.tv_usec) & 0xFFFFF))
/* ============================================================================
* DYNAMIC SLOT SLEEP TIMING CONTROL CONSTANTS
* ============================================================================ */
/* Dynamic Target Margin (adaptive to avoid late packets) */
#define MARGIN_TOLERANCE_US 20 // Deadband zone: +/- MARGIN_TOLERANCE_US us
#define MARGIN_TOLERANCE_LOCKED_US 800 // Wider deadband zone used after first sync lock
#define SLOT_ARRAY_SIZE 20 // TDD cycle slot count (Reduced to 20 for faster convergence)
typedef struct {
uint8_t* buffer;
@@ -70,19 +77,34 @@ typedef struct nfapi_vnf_p7_connection_info {
int32_t slot_offset_filtered;
uint16_t zero_count;
int32_t adjustment;
int32_t slot_adjustment;
int32_t us_adjustment;
int32_t insync_minor_adjustment;
int32_t insync_minor_adjustment_duration;
uint8_t sync_locked; // Flag: once offset converges within ±10, permanently stop adjusting
/* Periodic sync control */
uint32_t sync_slot_counter; // Counter for periodic sync
uint32_t sync_period_slots; // Period between syncs (configurable)
uint32_t previous_t1;
uint32_t previous_t2;
int32_t previous_sf_offset_filtered;
int32_t previous_slot_offset_filtered;
uint8_t initial_timinginfo_received;
int sfn_sf;
int sfn;
int slot;
int mu; // some 5G slot calculations need the numerology to know the number
// of slots
int slot_ahead;
uint16_t timing_window;
uint8_t timing_info_period;
struct timespec next_slot_time;
uint32_t slot_duration_us;
uint8_t running;
pthread_t thread;
pthread_mutex_t mutex;
pthread_cond_t initial_timinginfo_cond;
int socket;
struct sockaddr_in local_addr;
struct sockaddr_in remote_addr;
@@ -95,6 +117,24 @@ typedef struct nfapi_vnf_p7_connection_info {
struct nfapi_vnf_p7_connection_info* next;
int32_t pending_us; // Accumulated borrowed time (us) to be repaid incrementally
int32_t estimated_mean_late; // estimated mean delay
int32_t estimated_jitter_var; // estimated jitter variance
int32_t late_jitter; // Separate EWMA for late jitter
int32_t early_jitter; // Separate EWMA for early jitter
int32_t last_adjustment_steps; // How many slots we increased in last adjustment
int32_t last_adjustment_sfn; // SFN when we made the last upward adjustment
int32_t last_adjustment_slot; // Slot when we made the last upward adjustment
int32_t DM_EWMA_safe_period_count;
int32_t DM_EWMA_late_period_count;
int32_t DM_EWMA_risk_period_count;
int32_t DM_EWMA_last_target_s_ahead;
int32_t DM_EWMA_failure_debt_us;
int32_t DM_EWMA_risk_debt_us;
int32_t DM_EWMA_safe_margin_ewma_us;
int32_t DM_EWMA_jitter_pressure_ahead_us;
int32_t DM_EWMA_jitter_pressure_hold_ahead_us;
int32_t DM_EWMA_jitter_pressure_hold_slots;
} nfapi_vnf_p7_connection_info_t;
typedef struct vnf_p7_s {
@@ -137,5 +177,18 @@ int vnf_p7_pack_and_send_p7_msg(vnf_p7_t* vnf_p7, nfapi_p7_message_header_t* hea
void vnf_p7_release_msg(vnf_p7_t* vnf_p7, nfapi_p7_message_header_t* header);
void vnf_p7_release_pdu(vnf_p7_t* vnf_p7, void* pdu);
typedef struct {
int32_t worst_late;
int32_t worst_early;
uint32_t packet_slot; // Computed packet slot index in SLOT_ARRAY_SIZE
uint32_t pnf_reported_jitter; // Maximum jitter reported by PNF across message types
} vnf_timing_stats_t;
/* Function Declaration */
// Extract timing info points from a timing_info message
// Returns the number of valid stats extracted (0-8)
int vnf_nr_extract_timing_info(const nfapi_nr_timing_info_t *ind,
nfapi_vnf_p7_connection_info_t *p7_info,
vnf_timing_stats_t *out_stats);
#endif // _VNF_P7_H_

View File

@@ -33,7 +33,7 @@ typedef struct nfapi_vnf_phy_info
int phy_id; //phy_id
/*! Timing window */
uint8_t timing_window;
uint16_t timing_window;
/*! Timing info mode */
uint8_t timing_info_mode;
/*! Timing info period */
@@ -96,6 +96,13 @@ typedef struct nfapi_vnf_config
/*! List of configured phys */
nfapi_vnf_phy_info_t* phy_list;
/*! Timing window */
uint16_t timing_window;
/*! Timing info mode */
uint8_t timing_info_mode;
/*! Timing info period */
uint8_t timing_info_period;
/*! Configuration options for the p4 p5 pack unpack functions */
nfapi_p4_p5_codec_config_t codec_config;

View File

@@ -46,7 +46,7 @@ nfapi_vnf_config_t* nfapi_vnf_config_create()
_this->_public.codec_config.allocate = &malloc;
_this->_public.codec_config.deallocate = &free;
return (nfapi_vnf_config_t* )_this;
}
@@ -748,9 +748,9 @@ int nfapi_vnf_allocate_phy(nfapi_vnf_config_t* config, int p5_idx, uint16_t* phy
info->p5_idx = p5_idx;
info->phy_id = vnf->next_phy_id++;
info->timing_window = 30; // This seems to override what gets set by the user - why??? //TODO: Change in NR in terms of microsecends,what should be the value?
info->timing_info_mode = 0x03;
info->timing_info_period = 10;
info->timing_window = config->timing_window;
info->timing_info_mode = config->timing_info_mode;
info->timing_info_period = config->timing_info_period;
nfapi_vnf_phy_info_list_add(config, info);

File diff suppressed because it is too large Load Diff

View File

@@ -437,9 +437,13 @@ int nfapi_vnf_p7_add_pnf(nfapi_vnf_p7_config_t* config, const char* pnf_p7_addr,
node->dl_in_sync_period = 512;
//node->sfn_sf = 0;
node->sfn = 0;
node->slot = 0;
node->slot = 0;
node->min_sync_cycle_count = 8;
node->mu = mu;
node->timing_window = 4500;
node->timing_info_period = 1;
pthread_mutex_init(&node->mutex, NULL);
pthread_cond_init(&node->initial_timinginfo_cond, NULL);
#ifndef ENABLE_AERIAL
// save the remote endpoint information
node->remote_addr.sin_family = AF_INET;