Commit 2c4dac7b authored by yangjian's avatar yangjian

add web interface

parent e8ab3f56
......@@ -1828,6 +1828,8 @@ add_executable(nr-softmodem
${OPENAIR_DIR}/executables/nr-gnb.c
${OPENAIR_DIR}/executables/nr-ru.c
${OPENAIR_DIR}/executables/nr-softmodem.c
${OPENAIR_DIR}/executables/data_transfer.c
${OPENAIR_DIR}/executables/cJSON.c
${OPENAIR_DIR}/common/utils/lte/ue_power.c
${OPENAIR_DIR}/common/utils/lte/prach_utils.c
${PHY_INTERFACE_DIR}/queue_t.c
......@@ -1841,6 +1843,7 @@ target_link_libraries(nr-softmodem PRIVATE
ngap s1ap L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB SIMU
x2ap f1ap m2ap m3ap e1ap radio_common
time_management softmodem_common
microhttpd # apt install libmicrohttpd-dev
-Wl,--end-group z dl)
target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt)
......
This diff is collapsed.
This diff is collapsed.
#include "data_transfer.h"
#include <microhttpd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include "common/ran_context.h"
#include "openair1/PHY/defs_gNB.h"
#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h" // need to get info from MAC
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" // need to get info from MAC
#include "openair2/RRC/NR/rrc_gNB_UE_context.h" // need to get imsi from RRC
#include "softmodem-common.h"
#include "cJSON.h"
extern RAN_CONTEXT_t RC;
#define PORT 8888
int rb = 0;
// 构造 HTTP 响应的函数
int send_json_response(struct MHD_Connection *connection, const char *json_str)
{
struct MHD_Response *response;
int ret;
response = MHD_create_response_from_buffer(strlen(json_str),
(void*)json_str,
MHD_RESPMEM_MUST_COPY);
MHD_add_response_header(response, "Content-Type", "application/json");
ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
MHD_destroy_response(response);
return ret;
}
/**
* 计算 RSRQ (参考信号接收质量)
* @param rsrp_dBm RSRP 值,单位 dBm (例如 -90.0)
* @param rssi_dBm RSSI 值,单位 dBm (例如 -50.0)
* @param N 资源块 (RB) 数量 (例如 100)
* @return RSRQ 值,单位 dB;若输入无效则返回 -INFINITY 或 NaN
*/
static float calculate_rsrq(float rsrp_dBm, float rssi_dBm) {
// 1. 参数有效性检查
if (rb <= 0 || rssi_dBm == 0.0) { // RSSI 为 0 dBm 对应 1mW,理论上可能,但实际很少
// 更常见的无效情况是 rssi_dBm 极小(如 -140),这里仅做简单保护
return 0;
}
// 2. 将 dBm 转换为线性功率 (mW)
// P_mW = 10^(P_dBm / 10)
double rsrp_mW = pow(10.0, rsrp_dBm / 10.0);
double rssi_mW = pow(10.0, rssi_dBm / 10.0);
// 3. 计算比值
double ratio = rsrp_mW / rssi_mW; // 一般小于 1 的正数
// 4. 计算 N * 比值
double product = (double)rb * ratio;
// 5. 转换为 dB: 10 * log10(product)
// 注意:如果 product <= 0,log10 会出错,但 product 永远为正(只要输入有效)
double rsrq_dB = 10.0 * log10(product);
return rsrq_dB;
}
// 请求处理函数
int answer_to_connection(void *cls, struct MHD_Connection *connection,
const char *url, const char *method,
const char *version, const char *upload_data,
size_t *upload_data_size, void **con_cls)
{
(void)cls; (void)method; (void)version; (void)upload_data; (void)upload_data_size; (void)con_cls;
if (strcmp(url, "/stats") == 0) {
cJSON *root = cJSON_CreateArray();
if (root == NULL) {
return -1;
}
gNB_MAC_INST *gNB = RC.nrmac[0];
NR_SCHED_LOCK(&gNB->sched_lock);
UE_iterator(gNB->UE_info.connected_ue_list, UE) {
// rrc_gNB_ue_context_t *rrcue = rrc_gNB_get_ue_context_by_rnti_any_du(RC.nrrrc[0],UE->rnti);
// if (!rrcue) {
// continue;
// }
const int avg_rsrp = UE->mac_stats.num_rsrp_meas > 0 ? UE->mac_stats.cumul_rsrp / UE->mac_stats.num_rsrp_meas : 0;
const float rssi = (float)UE->UE_sched_ctrl.pusch_pc.avg_rssi/(-10.0f);
cJSON *obj = cJSON_CreateObject();
cJSON_AddNumberToObject(obj, "rnti", UE->rnti); //value
// cJSON_AddStringToObject(obj, "imsi", rrcue->ue_context.imsi); //string
cJSON_AddNumberToObject(obj, "dl_rate", UE->dl_rate); //value
cJSON_AddNumberToObject(obj, "ul_rate", UE->ul_rate); //value
cJSON_AddNumberToObject(obj, "cqi", UE->UE_sched_ctrl.CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb); //value
cJSON_AddNumberToObject(obj, "ri", UE->UE_sched_ctrl.CSI_report.cri_ri_li_pmi_cqi_report.ri+1); //value
cJSON_AddNumberToObject(obj, "phr", UE->UE_sched_ctrl.ph); //value
cJSON_AddNumberToObject(obj, "avg_rsrp", avg_rsrp); //value
cJSON_AddNumberToObject(obj, "rsrp", UE->mac_stats.num_rsrp_meas); //value
cJSON_AddNumberToObject(obj, "sinr", UE->mac_stats.num_sinr_meas); //value
cJSON_AddNumberToObject(obj, "avg_sinr", (float)(UE->mac_stats.num_sinr_meas > 0 ? UE->mac_stats.cumul_sinrx10 / UE->mac_stats.num_sinr_meas : 0)/10.0f); //value
cJSON_AddNumberToObject(obj, "ul_mcs", UE->UE_sched_ctrl.ul_bler_stats.mcs); //value
cJSON_AddNumberToObject(obj, "dl_mcs", UE->UE_sched_ctrl.dl_bler_stats.mcs); //value
cJSON_AddNumberToObject(obj, "pusch_snr", nr_mac_get_snr(&UE->UE_sched_ctrl.pusch_pc)); //value
cJSON_AddNumberToObject(obj, "pucch_snr", nr_mac_get_snr(&UE->UE_sched_ctrl.pucch_pc)); //value
cJSON_AddNumberToObject(obj, "rssi", rssi); //value
cJSON_AddNumberToObject(obj, "ul_bler", UE->UE_sched_ctrl.ul_bler_stats.bler); //value
cJSON_AddNumberToObject(obj, "dl_bler", UE->UE_sched_ctrl.dl_bler_stats.bler); //value
cJSON_AddNumberToObject(obj, "rsrq", calculate_rsrq(avg_rsrp,rssi)); //value
cJSON_AddItemToArray(root, obj);
}
NR_SCHED_UNLOCK(&gNB->sched_lock);
char *json_string = cJSON_Print(root);
int ret = send_json_response(connection, json_string);
if (json_string) {
free(json_string);
}
cJSON_Delete(root);
return ret;
} else {
const char *json = "{\"error\":\"Not Found\"}";
struct MHD_Response *response = MHD_create_response_from_buffer(strlen(json),
(void*)json,
MHD_RESPMEM_MUST_COPY);
MHD_add_response_header(response, "Content-Type", "application/json");
int ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
MHD_destroy_response(response);
return ret;
}
}
struct MHD_Daemon *httpdaemon;
int start_http_server(int dl_rb)
{
rb = dl_rb;
httpdaemon = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD,
PORT,
NULL, NULL,
&answer_to_connection, NULL,
MHD_OPTION_END);
if (httpdaemon == NULL) {
fprintf(stderr, "Failed to start HTTP server\n");
return 1;
}
return 0;
}
void stop_http_server()
{
MHD_stop_daemon(httpdaemon);
}
bool gnb_extract_imsi(const uint8_t* buffer, const uint32_t length, char* rrc_imsi)
{
if (length < 2) {
return false;
}
char imsi[15] = {};
uint8_t octet = 0;
uint8_t decoded_size = 0;
octet = *(buffer + decoded_size);
decoded_size++;
if (octet != 0x7e) return false;
octet = *(buffer + decoded_size);
decoded_size++;
if((octet & 0x0f) > 1) {
return false;
}
if((octet & 0x0f) == 1) {
decoded_size += 7;
}
// message type
octet = *(buffer + decoded_size);
decoded_size++;
if((octet != 0x41) && (octet != 0x5c)) {
return false;
}
if(octet == 0x41) {
decoded_size ++;
}
uint16_t mobility_id_len = ntohs(*(uint16_t*) (buffer + decoded_size));
decoded_size += sizeof(uint16_t);
uint8_t decoded_size_tmp = decoded_size;
octet = *(buffer + decoded_size);
decoded_size ++;
uint8_t mobility_id_type = octet & 0x07;
uint8_t format_imsi = (octet & 0x70) >> 4;
if((mobility_id_type != 1) || (format_imsi != 0)) {
return false;
}
// mcc mnc
octet = *(buffer + decoded_size);
decoded_size ++;
int mcc = 0;
int mnc = 0;
mcc += ((octet & 0x0f) * 100 + ((octet & 0xf0) >> 4) * 10);
octet = *(buffer + decoded_size);
decoded_size ++;
mcc += (octet & 0x0f);
if ((octet & 0xf0) == 0xf0) {
octet = *(buffer + decoded_size);
decoded_size ++;
mnc += ((octet & 0x0f) * 10 + ((octet & 0xf0) >> 4));
} else {
mnc += ((octet & 0xf0) >> 4);
octet = *(buffer + decoded_size);
decoded_size ++;
mnc += ((octet & 0x0f) * 100 + ((octet & 0xf0) >> 4) * 10);
}
int imsi_size = 0;
sprintf(imsi, "%03d", mcc);
imsi_size += 3;
sprintf(imsi+imsi_size, "%02d", mnc);
imsi_size += 2;
//Routing Indicator/Protection scheme Id/Home network public key identifier
decoded_size += 4;
// MSIN
uint8_t digit_low = 0;
uint8_t digit_high = 0;
int numMsin = decoded_size_tmp + mobility_id_len - decoded_size;
// LOG_I(NGAP,"********************** numMsin(%d) decoded_size_tmp(%d) mobility_id_len(%d) decoded_size(%d)\n", numMsin,decoded_size_tmp,mobility_id_len,decoded_size);
for (int i = 0; i < (numMsin - 1); i++) {
octet = *(buffer + decoded_size);
decoded_size ++;
digit_high = (octet & 0xf0) >> 4;
digit_low = octet & 0x0f;
imsi[imsi_size] = '0' + digit_low;
imsi_size ++;
imsi[imsi_size] = '0' + digit_high;
imsi_size ++;
}
octet = *(buffer + decoded_size);
decoded_size ++;
digit_high = (octet & 0xf0) >> 4;
digit_low = octet & 0x0f;
if (digit_high != 0x0f) {
imsi[imsi_size] = '0' + digit_low;
imsi_size ++;
imsi[imsi_size] = '0' + digit_high;
imsi_size ++;
} else {
imsi[imsi_size] = '0' + digit_low;
imsi_size ++;
}
// gNB_MAC_INST *gNB = RC.nrmac[RC.gNB[RC.nb_nr_inst-1]->Mod_id];
// NR_SCHED_LOCK(&gNB->sched_lock);
// UE_iterator(gNB->UE_info.connected_ue_list, UE) {
// if(rnti == UE->rnti) {
// strncpy(UE->imsi,imsi,15);
// break;
// }
// }
// NR_SCHED_UNLOCK(&gNB->sched_lock);
strncpy(rrc_imsi,imsi,15);
return true;
}
#ifndef _DATA_TRANSFER_H_
#define _DATA_TRANSFER_H_
#include <stdbool.h>
#include <stdint.h>
// bool gnb_extract_imsi(const uint8_t* buffer, const uint32_t length, uint16_t rnti);
bool gnb_extract_imsi(const uint8_t* buffer, const uint32_t length, char* rrc_imsi);
int start_http_server(int dl_rb);
void stop_http_server();
#endif /* _DATA_TRANSFER_H_ */
\ No newline at end of file
......@@ -69,6 +69,7 @@ unsigned short config_frames[4] = {2,9,11,13};
#include "x2ap_eNB.h"
#include "openair1/SCHED_NR/sched_nr.h"
#include "openair2/SDAP/nr_sdap/nr_sdap.h"
#include "data_transfer.h"
pthread_cond_t nfapi_sync_cond;
pthread_mutex_t nfapi_sync_mutex;
......@@ -695,6 +696,8 @@ int main( int argc, char **argv ) {
pthread_mutex_unlock(&sync_mutex);
}
start_http_server(RC.gNB[0]->frame_parms.N_RB_DL);
// wait for end of program
printf("TYPE <CTRL-C> TO TERMINATE\n");
// Sleep a while before checking all parameters have been used
......@@ -705,6 +708,8 @@ int main( int argc, char **argv ) {
itti_wait_tasks_end(NULL);
printf("Returned from ITTI signal handler\n");
stop_http_server();
nfapi_stop_l1();
if (RC.nb_nr_L1_inst > 0 || RC.nb_RU > 0)
......
......@@ -55,7 +55,13 @@ void *nrmac_stats_thread(void *arg) {
return NULL;
}
struct timeval tv_start = {0};
while (oai_exit == 0) {
struct timeval tv_end;
gettimeofday(&tv_end, NULL);
double elapsed = (tv_end.tv_sec - tv_start.tv_sec)+(tv_end.tv_usec - tv_start.tv_usec)/1000000.0;
tv_start = tv_end;
char *p = output;
NR_SCHED_LOCK(&gNB->sched_lock);
p += dump_mac_stats(gNB, p, end - p, false);
......@@ -68,6 +74,17 @@ void *nrmac_stats_thread(void *arg) {
p += print_meas_log(&gNB->rlc_data_req, "rlc_data_req", NULL, NULL, p, end - p);
p += print_meas_log(&gNB->nr_srs_ri_computation_timer, "UL-RI computation time", NULL, NULL, p, end - p);
p += print_meas_log(&gNB->nr_srs_tpmi_computation_timer, "UL-TPMI computation time", NULL, NULL, p, end - p);
UE_iterator(gNB->UE_info.connected_ue_list, UE) {
UE->dl_rate = (double)(UE->mac_stats.dl.lc_bytes[4]-UE->dl_current_bytes)/elapsed;
UE->ul_rate = (double)(UE->mac_stats.ul.lc_bytes[4]-UE->ul_current_bytes)/elapsed;
// printf("UE %04x: RATE: TX %14"PRIu64"(%f) RX %14"PRIu64"(%f) \n",
// UE->rnti, UE->dl_current_bytes,UE->dl_rate, UE->ul_current_bytes,UE->ul_rate);
UE->dl_current_bytes = UE->mac_stats.dl.lc_bytes[4];
UE->ul_current_bytes = UE->mac_stats.ul.lc_bytes[4];
}
NR_SCHED_UNLOCK(&gNB->sched_lock);
size_t len = p - output;
if (fwrite(output, len, 1, file) != 1 || fflush(file) != 0) {
......
......@@ -794,6 +794,10 @@ typedef struct NR_UE_info {
// dedicated BWP is always 1 from the UE's point of view, even if the gNB has multiple BWPs.
// The below ID is the "true" (non-consecutive) BWP ID from the gNB's point of view
NR_BWP_Id_t local_bwp_id;
uint64_t dl_current_bytes;
uint64_t ul_current_bytes;
double dl_rate;
double ul_rate;
} NR_UE_info_t;
typedef struct {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment