mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-17 06:30:31 +00:00
Compare commits
9 Commits
develop
...
nfapi-Dela
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1db365ed89 | ||
|
|
447b592924 | ||
|
|
9878057726 | ||
|
|
ffb3020014 | ||
|
|
d61244943d | ||
|
|
6c8093b9a6 | ||
|
|
21501ea2f8 | ||
|
|
94269ddca1 | ||
|
|
c42bb45be9 |
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user