Commit c1f4bb18 authored by frtabu's avatar frtabu

start implementing access to nrUE cpu measurments via telnet server, as ith lte enb

parent 7c985d86
Branches unavailable
2025.w12 2025.w11 2025.w10 2025.w09 2025.w08 2025.w07 2025.w06 2025.w05 2025.w04 2025.w03 2025.w02 2024.w51 2024.w50 2024.w49 2024.w48 2024.w47 2024.w46 2024.w45 2024.w44 2024.w43 2024.w42 2024.w41 2024.w40 2024.w39 2024.w38 2024.w36 2024.w35 2024.w34 2024.w33 2024.w32 2024.w31 2024.w30 2024.w29 2024.w28 2024.w27 2024.w26 2024.w25 2024.w24 2024.w23 2024.w22 2024.w21 2024.w18 2024.w17 2024.w16 2024.w15 2024.w14 2024.w13 2024.w12 2024.w11 2024.w10 2024.w09 2024.w08 2024.w06 2024.w05 2024.w04 2024.w03 2024.w02 2024.w01 2023.w51 2023.w50 2023.w49 2023.w48 2023.w47 2023.w45 2023.w43 2023.w42 2023.w41 2023.w40 2023.w39 2023.w38 2023.w37 2023.w36 2023.w34 2023.w33 2023.w32 2023.w31 2023.w30 2023.w29 2023.w28 2023.w27 2023.w26 2023.w25 2023.w24 2023.w23 2023.w22 2023.w21 2023.w20 2023.w19 2023.w18 2023.w18b 2023.w16 2023.w15 2023.w14 2023.w13 2023.w12 2023.w11 2023.w11b 2023.w10 2023.w10b 2023.w09 2023.w08 2023.w08b 2023.w07 2023.w06 2023.w05 2023.w03 2023.w02 2022.42 2022.41 2022.w51 2022.w50 2022.w49 2022.w48 2022.w47 2022.w46 2022.w45 2022.w43 2022.w42 2022.w42b 2022.w41 2022.w40 2022.w39 2022.w38 2022.w37 2022.w37b 2022.w36 2022.w35 2022.w33 2022.w32 2022.w31 2022.w31b 2022.w30 2022.w29 2022.w26 2022.w25 2022.w24 2022.w24b 2022.w23 2022.w22 2022.w21 2022.w20 2022.w19 2022.w18 2022.w17 2022.w15 2022.w15b 2022.w14a 2022.w13 2022.w13b 2022.w13a 2022.w12 2022.w10 2022.w09 2022.w09b 2022.w08 2022.w08b 2022.w07 2022.w07b 2022.w06 2022.w06a 2022.w05 2022.w05b 2022.w03_hotfix 2022.w03_b 2022.w02 2022.w01 2021.wk46 2021.w51_c 2021.w51_a 2021.w50_a 2021.w49_b 2021.w49_a 2021.w48 2021.w47 2021.w46 2021.w46-powder 2021.w45 2021.w45_b 2021.w44 2021.w43 v2.2.0 v2.1.0 v2.0.0 setparam flexran-eol ARC_1.3
No related merge requests found
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <dlfcn.h> #include <dlfcn.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <form.h>
#include "common/utils/load_module_shlib.h" #include "common/utils/load_module_shlib.h"
#include "common/config/config_userapi.h" #include "common/config/config_userapi.h"
#include "common/utils/threadPool/thread-pool.h" #include "common/utils/threadPool/thread-pool.h"
...@@ -62,25 +63,18 @@ ...@@ -62,25 +63,18 @@
#include "telnetsrv_proccmd.h" #include "telnetsrv_proccmd.h"
static char *telnet_defstatmod[] = {"softmodem","phy","loader","measur"}; static char *telnet_defstatmod[] = {"softmodem","phy","loader","measur"};
static telnetsrv_params_t telnetparams; static telnetsrv_params_t telnetparams;
#define TELNETSRV_LISTENADDR 0
#define TELNETSRV_LISTENPORT 1 #define TELNETSRV_OPTNAME_STATICMOD "staticmod"
#define TELNETSRV_PRIORITY 2 #define TELNETSRV_OPTNAME_SHRMOD "shrmod"
#define TELNETSRV_DEBUG 3
#define TELNETSRV_LOOPC 4
#define TELNETSRV_LOOPD 5
#define TELNETSRV_HISFILE 6
#define TELNETSRV_HISSIZE 7
#define TELNETSRV_PHYBSIZE 8
#define TELNETSRV_STATICMOD 9
#define TELNETSRV_SHRMOD 10
paramdef_t telnetoptions[] = { paramdef_t telnetoptions[] = {
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/* configuration parameters for telnet utility */ /* configuration parameters for telnet utility */
/* optname helpstr paramflags XXXptr defXXXval type numelt */ /* optname helpstr paramflags XXXptr defXXXval type numelt */
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
{"listenaddr", "<listen ip address>\n", 0, uptr:&telnetparams.listenaddr, defstrval:"0.0.0.0", TYPE_IPV4ADDR, 0 }, {"listenaddr", "<listen ip address>\n", 0, uptr:&telnetparams.listenaddr, defstrval:"0.0.0.0", TYPE_IPV4ADDR, 0 },
{"listenport", "<local port>\n", 0, uptr:&(telnetparams.listenport), defuintval:9090, TYPE_UINT, 0 }, {"listenport", "<local port>\n", 0, uptr:&(telnetparams.listenport), defuintval:9090, TYPE_UINT, 0 },
{"listenstdin", "enable input from stdin\n", PARAMFLAG_BOOL, uptr:&(telnetparams.listenstdin), defuintval:0, TYPE_UINT, 0 },
{"priority", "<scheduling policy (0-99)\n", 0, iptr:&telnetparams.priority, defuintval:0, TYPE_INT, 0 }, {"priority", "<scheduling policy (0-99)\n", 0, iptr:&telnetparams.priority, defuintval:0, TYPE_INT, 0 },
{"debug", "<debug level>\n", 0, uptr:NULL, defuintval:0, TYPE_UINT, 0 }, {"debug", "<debug level>\n", 0, uptr:NULL, defuintval:0, TYPE_UINT, 0 },
{"loopcount", "<loop command iterations>\n", 0, uptr:&(telnetparams.loopcount), defuintval:10, TYPE_UINT, 0 }, {"loopcount", "<loop command iterations>\n", 0, uptr:&(telnetparams.loopcount), defuintval:10, TYPE_UINT, 0 },
...@@ -88,8 +82,8 @@ paramdef_t telnetoptions[] = { ...@@ -88,8 +82,8 @@ paramdef_t telnetoptions[] = {
{"histfile", "<history file name>\n", PARAMFLAG_NOFREE, strptr:&(telnetparams.histfile), defstrval:"oaitelnet.history", TYPE_STRING, 0 }, {"histfile", "<history file name>\n", PARAMFLAG_NOFREE, strptr:&(telnetparams.histfile), defstrval:"oaitelnet.history", TYPE_STRING, 0 },
{"histsize", "<history sizes>\n", 0, iptr:&(telnetparams.histsize), defuintval:50, TYPE_INT, 0 }, {"histsize", "<history sizes>\n", 0, iptr:&(telnetparams.histsize), defuintval:50, TYPE_INT, 0 },
{"phypbsize", "<phy dump buff size (bytes)>\n",0, uptr:&(telnetparams.phyprntbuff_size),defuintval:65000, TYPE_UINT, 0 }, {"phypbsize", "<phy dump buff size (bytes)>\n",0, uptr:&(telnetparams.phyprntbuff_size),defuintval:65000, TYPE_UINT, 0 },
{"staticmod", "<static modules selection>\n", 0, strlistptr:NULL, defstrlistval:telnet_defstatmod,TYPE_STRINGLIST,(sizeof(telnet_defstatmod)/sizeof(char *))}, {TELNETSRV_OPTNAME_STATICMOD, "<static modules selection>\n", 0, strlistptr:NULL, defstrlistval:telnet_defstatmod,TYPE_STRINGLIST,(sizeof(telnet_defstatmod)/sizeof(char *))},
{"shrmod", "<dynamic modules selection>\n", 0, strlistptr:NULL, defstrlistval:NULL,TYPE_STRINGLIST,0 } {TELNETSRV_OPTNAME_SHRMOD, "<dynamic modules selection>\n", 0, strlistptr:NULL, defstrlistval:NULL,TYPE_STRINGLIST,0 }
}; };
int get_phybsize(void) { int get_phybsize(void) {
...@@ -493,7 +487,11 @@ int process_command(char *buf) { ...@@ -493,7 +487,11 @@ int process_command(char *buf) {
bufbck=strdup(buf); bufbck=strdup(buf);
rt=CMDSTATUS_NOTFOUND; rt=CMDSTATUS_NOTFOUND;
<<<<<<< HEAD
j = sscanf(buf,"%19s %19s %m[^\t\n]",modulename,cmd,&cmdb); j = sscanf(buf,"%19s %19s %m[^\t\n]",modulename,cmd,&cmdb);
=======
j = sscanf(buf,"%19s %19s %2048[^\t\n]",modulename,cmd,cmdb);
>>>>>>> start implementing access to nrUE cpu measurments via telnet server, as ith lte enb
if (telnetparams.telnetdbg > 0) if (telnetparams.telnetdbg > 0)
printf("process_command: %i words, module=%s cmd=%s, parameters= %s\n",j,modulename,cmd,cmdb); printf("process_command: %i words, module=%s cmd=%s, parameters= %s\n",j,modulename,cmd,cmdb);
...@@ -682,6 +680,117 @@ void run_telnetsrv(void) { ...@@ -682,6 +680,117 @@ void run_telnetsrv(void) {
return; return;
} }
void run_telnetclt(void) {
int sock;
struct sockaddr_in name;
pthread_setname_np(pthread_self(), "telnetclt");
set_sched(pthread_self(),0,telnetparams.priority);
sock = socket(AF_INET, SOCK_STREAM, 0);
// FIELD *field[4];
if (sock < 0)
fprintf(stderr,"[TELNETSRV] Error %s on socket call\n",strerror(errno));
char prompt[sizeof(TELNET_PROMPT_PREFIX)+10];
sprintf(prompt,"%s_%s> ",TELNET_PROMPT_PREFIX,get_softmodem_function(NULL));
name.sin_family = AF_INET;
struct in_addr addr;
inet_aton("127.0.0.1", &addr) ;
name.sin_addr.s_addr = addr.s_addr;
name.sin_port = htons((unsigned short)(telnetparams.listenport));
if(connect(sock, (void *) &name, sizeof(name)))
fprintf(stderr,"[TELNETSRV] Error %s on connect call\n",strerror(errno));
// (void) initscr(); /* initialize the curses library */
// keypad(stdscr, TRUE); /* enable keyboard mapping */
// (void) nonl(); /* tell curses not to do NL->CR/NL on output */
// (void) cbreak(); /* take input chars one at a time, no wait for \n */
// (void) echo(); /* echo input - in color */
// WINDOW *inputw = newwin(10, 132, 0, 0);
// int row,col;
// getmaxyx(stdscr,row,col);
// field[0] = new_field((row-2)/2, col, 0, 0, 0, 0);
// field[1] = new_field((row-2)/2, col, (row-2)/2, 0, 0, 0);
// field[2] = new_field(1, col, row-2, 0, 0, 0);
// field[3] = NULL;
/* Set field options */
// field_opts_off(field[0], O_ACTIVE);
// field_opts_off(field[1], O_ACTIVE);
// set_field_back(field[0], A_UNDERLINE); /* Print a line for the option */
// field_opts_off(field[0], O_AUTOSKIP); /* Don't go to next field when this */
/* Field is filled up */
// set_field_back(field[1], A_UNDERLINE);
// field_opts_off(field[1], O_AUTOSKIP);
/* Create the form and post it */
// FORM *my_form = new_form(field);
// post_form(my_form);
// refresh();
// echo();
struct timeval ts;
ts.tv_sec = 1; // 1 second
ts.tv_usec = 0;
while (1) {
// select setup
fd_set fds;
FD_ZERO(&fds);
FD_SET(sock, &fds);
FD_SET(STDIN_FILENO , &fds);
// wait for data
int nready = select(sock + 1, &fds, (fd_set *) 0, (fd_set *) 0, &ts);
// mvprintw(row-2,0,"select: %i \n",nready);
if (nready < 0) {
perror("select. Error");
break;
}
else if (nready == 0) {
ts.tv_sec = 1; // 1 second
ts.tv_usec = 0;
}
else if ( FD_ISSET(sock, &fds)) {
int rv;
char inbuf[TELNET_MAX_MSGLENGTH*2];
memset(inbuf,0,sizeof(inbuf));
rv = recv(sock , inbuf , sizeof(inbuf)-1 , 0);
if (rv > 0) {
printf("%s",inbuf);
}
else if (rv == 0) {
printf("Connection closed by the remote end\n\r");
break;
}
else {
perror("recv error");
break;
}
}
else if (FD_ISSET(STDIN_FILENO , &fds)) {
char *inbuf=NULL;
size_t inlen=0;
inlen = getline( &inbuf,&inlen, stdin);
// mvprintw(row-1,0,inbuf);
if ( inlen > 0 ) {
if ( send(sock, inbuf,inlen, 0) < 0)
break;
}
free(inbuf);
// refresh();
}
}
/* nocbreak();
unpost_form(my_form);
free_form(my_form);
free_field(field[0]);
free_field(field[1]);
free_field(field[2]);
endwin();*/
close(sock);
return;
} /* run_telnetclt */
void poll_telnetcmdq(void *qid, void *arg) { void poll_telnetcmdq(void *qid, void *arg) {
notifiedFIFO_elt_t *msg = pollNotifiedFIFO((notifiedFIFO_t *)qid); notifiedFIFO_elt_t *msg = pollNotifiedFIFO((notifiedFIFO_t *)qid);
...@@ -720,10 +829,10 @@ void exec_moduleinit(char *modname) { ...@@ -720,10 +829,10 @@ void exec_moduleinit(char *modname) {
int add_embeddedmodules(void) { int add_embeddedmodules(void) {
int ret=0; int ret=0;
int pindex = config_paramidx_fromname(telnetoptions,sizeof(telnetoptions)/sizeof(paramdef_t), TELNETSRV_OPTNAME_STATICMOD);
for(int i=0; i<telnetoptions[TELNETSRV_STATICMOD].numelt; i++) { for(int i=0; i<telnetoptions[pindex].numelt; i++) {
ret++; ret++;
exec_moduleinit(telnetoptions[TELNETSRV_STATICMOD].strlistptr[i]); exec_moduleinit(telnetoptions[pindex].strlistptr[i]);
} }
return ret; return ret;
...@@ -733,16 +842,16 @@ int add_sharedmodules(void) { ...@@ -733,16 +842,16 @@ int add_sharedmodules(void) {
char initfunc[TELNET_CMD_MAXSIZE+9]; char initfunc[TELNET_CMD_MAXSIZE+9];
void (*fptr)(void); void (*fptr)(void);
int ret=0; int ret=0;
int pindex = config_paramidx_fromname(telnetoptions,sizeof(telnetoptions)/sizeof(paramdef_t), TELNETSRV_OPTNAME_SHRMOD);
for(int i=0; i<telnetoptions[TELNETSRV_SHRMOD].numelt; i++) { for(int i=0; i<telnetoptions[pindex].numelt; i++) {
sprintf(initfunc,"add_%s_cmds",telnetoptions[TELNETSRV_SHRMOD].strlistptr[i]); sprintf(initfunc,"add_%s_cmds",telnetoptions[pindex].strlistptr[i]);
fptr = dlsym(RTLD_DEFAULT,initfunc); fptr = dlsym(RTLD_DEFAULT,initfunc);
if ( fptr != NULL) { if ( fptr != NULL) {
fptr(); fptr();
ret++; ret++;
} else { } else {
fprintf(stderr,"[TELNETSRV] couldn't find %s for module %s \n",initfunc,telnetoptions[TELNETSRV_STATICMOD].strlistptr[i]); fprintf(stderr,"[TELNETSRV] couldn't find %s for module %s \n",initfunc,telnetoptions[pindex].strlistptr[i]);
} }
} }
...@@ -767,6 +876,12 @@ int telnetsrv_autoinit(void) { ...@@ -767,6 +876,12 @@ int telnetsrv_autoinit(void) {
add_telnetcmd("telnet", telnet_vardef, telnet_cmdarray); add_telnetcmd("telnet", telnet_vardef, telnet_cmdarray);
add_embeddedmodules(); add_embeddedmodules();
if ( telnetparams.listenstdin ) {
if(pthread_create(&telnetparams.telnetclt_pthread,NULL, (void *(*)(void *))run_telnetclt, NULL) != 0) {
fprintf(stderr,"[TELNETSRV] Error %s on pthread_create f() run_telnetclt \n",strerror(errno));
return -1;
}
}
return 0; return 0;
} }
......
...@@ -108,6 +108,7 @@ typedef struct cmdparser { ...@@ -108,6 +108,7 @@ typedef struct cmdparser {
/* global variables used by the telnet server */ /* global variables used by the telnet server */
typedef struct { typedef struct {
pthread_t telnet_pthread; // thread id of the telnet server pthread_t telnet_pthread; // thread id of the telnet server
pthread_t telnetclt_pthread; // thread id of the telnet client (used when listenstdin set to true)
int telnetdbg; // debug level of the server int telnetdbg; // debug level of the server
int priority; // server running priority int priority; // server running priority
char *histfile; // command history char *histfile; // command history
...@@ -119,6 +120,7 @@ typedef struct { ...@@ -119,6 +120,7 @@ typedef struct {
char msgbuff[TELNET_MAX_MSGLENGTH]; // internal buffer of the client_printf function which is used to print to the client terminal */ char msgbuff[TELNET_MAX_MSGLENGTH]; // internal buffer of the client_printf function which is used to print to the client terminal */
unsigned int listenport; // ip port the telnet server is listening on unsigned int listenport; // ip port the telnet server is listening on
unsigned int listenaddr; // ip address the telnet server is listening on unsigned int listenaddr; // ip address the telnet server is listening on
unsigned int listenstdin; // enable command input from stdin
unsigned int loopcount; // loop command param: number of loop iteration unsigned int loopcount; // loop command param: number of loop iteration
unsigned int loopdelay; // loop command param: delay in ms between 2 iterations unsigned int loopdelay; // loop command param: delay in ms between 2 iterations
unsigned int phyprntbuff_size; // for phy module, dump_eNB_stats function buffer size unsigned int phyprntbuff_size; // for phy module, dump_eNB_stats function buffer size
......
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file common/utils/telnetsrv/telnetsrv_nrue_measurements.c
* \brief: implementation of telnet commands related to nrUE measurments
* \author Francois TABURET
* \date 2021
* \version 0.1
* \company NOKIA BellLabs France
* \email: francois.taburet@nokia-bell-labs.com
* \note
* \warning
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#define TELNETSERVERCODE
#include "telnetsrv.h"
#include "common/utils/LOG/log.h"
#include "common/config/config_userapi.h"
#include "telnetsrv_measurements.h"
#include "telnetsrv_ltemeasur_def.h"
#include "telnetsrv_cpumeasur_def.h"
#include "openair2/LAYER2/NR_MAC_UE/mac_defs.h"
#include "openair1/PHY/phy_extern_nr_ue.h"
void measurcmd_display_macstats(telnet_printfunc_t prnt);
void measurcmd_display_macstats_ue(telnet_printfunc_t prnt);
void measurcmd_display_rlcstats(telnet_printfunc_t prnt);
void measurcmd_display_phycpu(telnet_printfunc_t prnt);
void measurcmd_display_maccpu(telnet_printfunc_t prnt);
void measurcmd_display_pdcpcpu(telnet_printfunc_t prnt);
static telnet_measurgroupdef_t nrUEmeasurgroups[] = {
// {"ue", GROUP_LTESTATS,0, measurcmd_display_macstats, {NULL}},
// {"rlc", GROUP_LTESTATS,0, measurcmd_display_rlcstats, {NULL}},
{"phycpu",GROUP_CPUSTATS,0, measurcmd_display_phycpu, {NULL}},
// {"maccpu",GROUP_CPUSTATS,0, measurcmd_display_maccpu, {NULL}},
// {"pdcpcpu",GROUP_CPUSTATS,0, measurcmd_display_pdcpcpu, {NULL}},
};
#define TELNET_NUM_NRUEMEASURGROUPS (sizeof(nrUEmeasurgroups)/sizeof(telnet_measurgroupdef_t))
static double cpufreq;
#define HDR "---------------------------------"
int get_measurgroups(telnet_measurgroupdef_t **measurgroups) {
*measurgroups = nrUEmeasurgroups;
return TELNET_NUM_NRUEMEASURGROUPS;
}
void measurcmd_display_phycpu(telnet_printfunc_t prnt) {
PHY_VARS_NR_UE *UE = PHY_vars_UE_g[0][0];
telnet_cpumeasurdef_t cpumeasur[]=CPU_PHYNRUE_MEASURE;
prnt("%s cpu (%1.1g GHz) measurements: PHY (cpustats %s) %s\n",HDR,cpufreq,
PRINT_CPUMEAS_STATE,HDR);
measurcmd_display_cpumeasures(prnt, cpumeasur, sizeof(cpumeasur)/sizeof(telnet_cpumeasurdef_t));
}
/*
void measurcmd_display_maccpu(telnet_printfunc_t prnt) {
eNB_MAC_INST *macvars = RC.mac[eNB_id];
telnet_cpumeasurdef_t cpumeasur[]=CPU_MACENB_MEASURE;
prnt("%s cpu (%1.1g GHz) measurements: MAC (cpustats %s) %s\n",HDR,cpufreq,
PRINT_CPUMEAS_STATE,HDR);
measurcmd_display_cpumeasures(prnt, cpumeasur, sizeof(cpumeasur)/sizeof(telnet_cpumeasurdef_t));
}
void measurcmd_display_pdcpcpu(telnet_printfunc_t prnt) {
pdcp_stats_t *pdcpvars = &(eNB_pdcp_stats[eNB_id]);
telnet_cpumeasurdef_t cpumeasur[]=CPU_PDCPENB_MEASURE;
prnt("%s cpu (%1.1g GHz) measurements: PDCP (cpustats %s) %s \n",HDR,cpufreq,
PRINT_CPUMEAS_STATE,HDR);
measurcmd_display_cpumeasures(prnt, cpumeasur, sizeof(cpumeasur)/sizeof(telnet_cpumeasurdef_t));
}
//----------------------------------------------------------------------------------------------------
void measurcmd_display_macstats_ue(telnet_printfunc_t prnt) {
UE_info_t *UE_info = &(RC.mac[eNB_id]->UE_info);
for (int UE_id=UE_info->list.head; UE_id>=0; UE_id=UE_info->list.next[UE_id]) {
for (int i=0; i<UE_info->numactiveCCs[UE_id]; i++) {
int CC_id = UE_info->ordered_CCids[i][UE_id];
prnt("%s UE %i Id %i CCid %i %s\n",HDR,i,UE_id,CC_id,HDR);
eNB_UE_STATS *macuestatptr = &(UE_info->eNB_UE_stats[CC_id][UE_id]);
telnet_ltemeasurdef_t statsptr[]=LTEMAC_UEMEASURE;
measurcmd_display_measures(prnt, statsptr, sizeof(statsptr)/sizeof(telnet_ltemeasurdef_t));
}
}
} // measurcmd_display_macstats_ue
void measurcmd_display_macstats(telnet_printfunc_t prnt) {
for (int CC_id=0 ; CC_id < MAX_NUM_CCs; CC_id++) {
eNB_STATS *macstatptr=&(RC.mac[eNB_id]->eNB_stats[CC_id]);
telnet_ltemeasurdef_t statsptr[]=LTEMAC_MEASURE;
prnt("%s eNB %i mac stats CC %i frame %u %s\n",
HDR, eNB_id, CC_id, RC.mac[eNB_id]->frame,HDR);
measurcmd_display_measures(prnt,statsptr,sizeof(statsptr)/sizeof(telnet_ltemeasurdef_t));
}
} // measurcmd_display_macstats
void measurcmd_display_one_rlcstat(telnet_printfunc_t prnt, int UE_id, telnet_ltemeasurdef_t *statsptr, int num_rlcmeasure, unsigned int *rlcstats,
char *rbid_str, protocol_ctxt_t *ctxt, const srb_flag_t srb_flagP, const rb_id_t rb_idP)
{
int rlc_status = rlc_stat_req(ctxt,srb_flagP,rb_idP,
rlcstats, rlcstats+1, rlcstats+2, rlcstats+3, rlcstats+4, rlcstats+5,
rlcstats+6, rlcstats+7, rlcstats+8, rlcstats+9, rlcstats+10, rlcstats+11,
rlcstats+12, rlcstats+13, rlcstats+14, rlcstats+15, rlcstats+16, rlcstats+17,
rlcstats+18, rlcstats+19, rlcstats+20, rlcstats+21, rlcstats+22, rlcstats+23,
rlcstats+24, rlcstats+25, rlcstats+26, rlcstats+27);
if (rlc_status == RLC_OP_STATUS_OK) {
prnt("%s UE %i RLC %s mode %s %s\n",HDR,UE_id, rbid_str,
(rlcstats[0]==RLC_MODE_AM)? "AM": (rlcstats[0]==RLC_MODE_UM)?"UM":"NONE",HDR);
measurcmd_display_measures(prnt, statsptr, num_rlcmeasure);
}
} // status measurcmd_rlc_stat_req
void measurcmd_display_rlcstats(telnet_printfunc_t prnt) {
protocol_ctxt_t ctxt;
UE_info_t *UE_info = &(RC.mac[eNB_id]->UE_info);
telnet_ltemeasurdef_t statsptr[]=LTE_RLCMEASURE;
int num_rlcmeasure = sizeof(statsptr)/sizeof(telnet_ltemeasurdef_t );
unsigned int *rlcstats = malloc(num_rlcmeasure*sizeof(unsigned int));
eNB_MAC_INST *eNB = RC.mac[eNB_id];
for(int i=0; i <num_rlcmeasure ; i++) {
statsptr[i].vptr = rlcstats + i;
}
for (int UE_id=UE_info->list.head; UE_id>=0; UE_id=UE_info->list.next[UE_id]) {
#define NB_eNB_INST 1
PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt,eNB_id, ENB_FLAG_YES,UE_info->eNB_UE_stats[0][UE_id].crnti,
eNB->frame,eNB->subframe,eNB_id);
measurcmd_display_one_rlcstat(prnt, UE_id, statsptr, num_rlcmeasure, rlcstats, "DCCH", &ctxt, SRB_FLAG_YES, DCCH);
measurcmd_display_one_rlcstat(prnt, UE_id, statsptr, num_rlcmeasure, rlcstats, "DTCH", &ctxt, SRB_FLAG_NO, DTCH-2);
}
} // measurcmd_display_macstats_ue
*/
...@@ -8,7 +8,7 @@ set(TELNETSRV_SOURCE ...@@ -8,7 +8,7 @@ set(TELNETSRV_SOURCE
) )
add_library(telnetsrv MODULE ${TELNETSRV_SOURCE} ) add_library(telnetsrv MODULE ${TELNETSRV_SOURCE} )
target_link_libraries(telnetsrv PRIVATE history) target_link_libraries(telnetsrv PRIVATE history ncurses form )
foreach(TELNETLIB enb gnb 4gUE 5gUE) foreach(TELNETLIB enb gnb 4gUE 5gUE)
set(TELNETLIB_SRCS "") set(TELNETLIB_SRCS "")
......
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
* \ strucures arrays which are then used by the display functions * \ strucures arrays which are then used by the display functions
* \ in telnetsrv_measurements.c. * \ in telnetsrv_measurements.c.
* \author Francois TABURET * \author Francois TABURET
* \date 2019 * \date 2021
* \version 0.1 * \version 0.2
* \company NOKIA BellLabs France * \company NOKIA BellLabs France
* \email: francois.taburet@nokia-bell-labs.com * \email: francois.taburet@nokia-bell-labs.com
* \note * \note
...@@ -37,67 +37,124 @@ ...@@ -37,67 +37,124 @@
#define CPU_PHYENB_MEASURE \ #define CPU_PHYENB_MEASURE \
{ \ { \
{"phy_proc_tx", &(phyvars->phy_proc_tx),0},\ {"phy_proc_tx", &(phyvars->phy_proc_tx),0,1},\
{"phy_proc_rx", &(phyvars->phy_proc_rx),0},\ {"phy_proc_rx", &(phyvars->phy_proc_rx),0,1},\
{"rx_prach", &(phyvars->rx_prach),0},\ {"rx_prach", &(phyvars->rx_prach),0,1},\
{"ofdm_mod", &(phyvars->ofdm_mod_stats),0},\ {"ofdm_mod", &(phyvars->ofdm_mod_stats),0,1},\
{"dlsch_common_and_dci", &(phyvars->dlsch_common_and_dci),0},\ {"dlsch_common_and_dci", &(phyvars->dlsch_common_and_dci),0,1},\
{"dlsch_ue_specific", &(phyvars->dlsch_ue_specific),0},\ {"dlsch_ue_specific", &(phyvars->dlsch_ue_specific),0,1},\
{"dlsch_encoding", &(phyvars->dlsch_encoding_stats),0},\ {"dlsch_encoding", &(phyvars->dlsch_encoding_stats),0,1},\
{"dlsch_modulation", &(phyvars->dlsch_modulation_stats),0},\ {"dlsch_modulation", &(phyvars->dlsch_modulation_stats),0,1},\
{"dlsch_scrambling", &(phyvars->dlsch_scrambling_stats),0},\ {"dlsch_scrambling", &(phyvars->dlsch_scrambling_stats),0,1},\
{"dlsch_rate_matching", &(phyvars->dlsch_rate_matching_stats),0},\ {"dlsch_rate_matching", &(phyvars->dlsch_rate_matching_stats),0,1},\
{"dlsch_turbo_encod_prep", &(phyvars->dlsch_turbo_encoding_preperation_stats),0},\ {"dlsch_turbo_encod_prep", &(phyvars->dlsch_turbo_encoding_preperation_stats),0,1},\
{"dlsch_turbo_encod_segm", &(phyvars->dlsch_turbo_encoding_segmentation_stats),0},\ {"dlsch_turbo_encod_segm", &(phyvars->dlsch_turbo_encoding_segmentation_stats),0,1},\
{"dlsch_turbo_encod", &(phyvars->dlsch_turbo_encoding_stats),0},\ {"dlsch_turbo_encod", &(phyvars->dlsch_turbo_encoding_stats),0,1},\
{"dlsch_turbo_encod_waiting", &(phyvars->dlsch_turbo_encoding_waiting_stats),0},\ {"dlsch_turbo_encod_waiting", &(phyvars->dlsch_turbo_encoding_waiting_stats),0,1},\
{"dlsch_turbo_encod_signal", &(phyvars->dlsch_turbo_encoding_signal_stats),0},\ {"dlsch_turbo_encod_signal", &(phyvars->dlsch_turbo_encoding_signal_stats),0,1},\
{"dlsch_turbo_encod_main", &(phyvars->dlsch_turbo_encoding_main_stats),0},\ {"dlsch_turbo_encod_main", &(phyvars->dlsch_turbo_encoding_main_stats),0,1},\
{"dlsch_turbo_encod_wakeup0", &(phyvars->dlsch_turbo_encoding_wakeup_stats0),0},\ {"dlsch_turbo_encod_wakeup0", &(phyvars->dlsch_turbo_encoding_wakeup_stats0),0,1},\
{"dlsch_turbo_encod_wakeup1", &(phyvars->dlsch_turbo_encoding_wakeup_stats1),0},\ {"dlsch_turbo_encod_wakeup1", &(phyvars->dlsch_turbo_encoding_wakeup_stats1),0,1},\
{"dlsch_interleaving", &(phyvars->dlsch_interleaving_stats),0},\ {"dlsch_interleaving", &(phyvars->dlsch_interleaving_stats),0,1},\
{"rx_dft", &(phyvars->rx_dft_stats),0},\ {"rx_dft", &(phyvars->rx_dft_stats),0,1},\
{"ulsch_channel_estimation", &(phyvars->ulsch_channel_estimation_stats),0},\ {"ulsch_channel_estimation", &(phyvars->ulsch_channel_estimation_stats),0,1},\
{"ulsch_freq_offset_estimation", &(phyvars->ulsch_freq_offset_estimation_stats),0},\ {"ulsch_freq_offset_estimation", &(phyvars->ulsch_freq_offset_estimation_stats),0,1},\
{"ulsch_decoding", &(phyvars->ulsch_decoding_stats),0},\ {"ulsch_decoding", &(phyvars->ulsch_decoding_stats),0,1},\
{"ulsch_demodulation", &(phyvars->ulsch_demodulation_stats),0},\ {"ulsch_demodulation", &(phyvars->ulsch_demodulation_stats),0,1},\
{"ulsch_rate_unmatching", &(phyvars->ulsch_rate_unmatching_stats),0},\ {"ulsch_rate_unmatching", &(phyvars->ulsch_rate_unmatching_stats),0,1},\
{"ulsch_turbo_decoding", &(phyvars->ulsch_turbo_decoding_stats),0},\ {"ulsch_turbo_decoding", &(phyvars->ulsch_turbo_decoding_stats),0,1},\
{"ulsch_deinterleaving", &(phyvars->ulsch_deinterleaving_stats),0},\ {"ulsch_deinterleaving", &(phyvars->ulsch_deinterleaving_stats),0,1},\
{"ulsch_demultiplexing", &(phyvars->ulsch_demultiplexing_stats),0},\ {"ulsch_demultiplexing", &(phyvars->ulsch_demultiplexing_stats),0,1},\
{"ulsch_llr", &(phyvars->ulsch_llr_stats),0},\ {"ulsch_llr", &(phyvars->ulsch_llr_stats),0,1},\
{"ulsch_tc_init", &(phyvars->ulsch_tc_init_stats),0},\ {"ulsch_tc_init", &(phyvars->ulsch_tc_init_stats),0,1},\
{"ulsch_tc_alpha", &(phyvars->ulsch_tc_alpha_stats),0},\ {"ulsch_tc_alpha", &(phyvars->ulsch_tc_alpha_stats),0,1},\
{"ulsch_tc_beta", &(phyvars->ulsch_tc_beta_stats),0},\ {"ulsch_tc_beta", &(phyvars->ulsch_tc_beta_stats),0,1},\
{"ulsch_tc_gamma", &(phyvars->ulsch_tc_gamma_stats),0},\ {"ulsch_tc_gamma", &(phyvars->ulsch_tc_gamma_stats),0,1},\
{"ulsch_tc_ext", &(phyvars->ulsch_tc_ext_stats),0},\ {"ulsch_tc_ext", &(phyvars->ulsch_tc_ext_stats),0,1},\
{"ulsch_tc_intl1", &(phyvars->ulsch_tc_intl1_stats),0},\ {"ulsch_tc_intl1", &(phyvars->ulsch_tc_intl1_stats),0,1},\
{"ulsch_tc_intl2", &(phyvars->ulsch_tc_intl2_stats),0},\ {"ulsch_tc_intl2", &(phyvars->ulsch_tc_intl2_stats),0,1},\
} }
#define CPU_MACENB_MEASURE \ #define CPU_MACENB_MEASURE \
{ \ { \
{"eNB_scheduler", &(macvars->eNB_scheduler),0},\ {"eNB_scheduler", &(macvars->eNB_scheduler),0,1},\
{"schedule_si", &(macvars->schedule_si),0},\ {"schedule_si", &(macvars->schedule_si),0,1},\
{"schedule_ra", &(macvars->schedule_ra),0},\ {"schedule_ra", &(macvars->schedule_ra),0,1},\
{"schedule_ulsch", &(macvars->schedule_ulsch),0},\ {"schedule_ulsch", &(macvars->schedule_ulsch),0,1},\
{"fill_DLSCH_dci", &(macvars->fill_DLSCH_dci),0},\ {"fill_DLSCH_dci", &(macvars->fill_DLSCH_dci),0,1},\
{"schedule_dlsch_pre", &(macvars->schedule_dlsch_preprocessor),0},\ {"schedule_dlsch_pre", &(macvars->schedule_dlsch_preprocessor),0,1},\
{"schedule_dlsch", &(macvars->schedule_dlsch),0},\ {"schedule_dlsch", &(macvars->schedule_dlsch),0,1},\
{"schedule_mch", &(macvars->schedule_mch),0},\ {"schedule_mch", &(macvars->schedule_mch),0,1},\
{"rx_ulsch_sdu", &(macvars->rx_ulsch_sdu),0},\ {"rx_ulsch_sdu", &(macvars->rx_ulsch_sdu),0,1},\
{"schedule_pch", &(macvars->schedule_pch),0},\ {"schedule_pch", &(macvars->schedule_pch),0,1},\
} }
#define CPU_PDCPENB_MEASURE \ #define CPU_PDCPENB_MEASURE \
{ \ { \
{"pdcp_run", &(pdcpvars->pdcp_run),0},\ {"pdcp_run", &(pdcpvars->pdcp_run),0,1},\
{"data_req", &(pdcpvars->data_req),0},\ {"data_req", &(pdcpvars->data_req),0,1},\
{"data_ind", &(pdcpvars->data_ind),0},\ {"data_ind", &(pdcpvars->data_ind),0,1},\
{"apply_security", &(pdcpvars->apply_security),0},\ {"apply_security", &(pdcpvars->apply_security),0,1},\
{"validate_security", &(pdcpvars->validate_security),0},\ {"validate_security", &(pdcpvars->validate_security),0,1},\
{"pdcp_ip", &(pdcpvars->pdcp_ip),0},\ {"pdcp_ip", &(pdcpvars->pdcp_ip),0,1},\
{"ip_pdcp", &(pdcpvars->ip_pdcp),0},\ {"ip_pdcp", &(pdcpvars->ip_pdcp),0,1},\
} }
/* from openair1/PHY/defs_nr_UE.h */
// {"ue_front_end_stat[RX_NB_TH]", &(UE->
// {"ue_front_end_per_slot_stat[RX_NB_TH][LTE_SLOTS_PER_SUBFRAME]", &(UE->
// {"pdcch_procedures_stat[RX_NB_TH]", &(UE->
// {"pdsch_procedures_stat[RX_NB_TH]", &(UE->
// {"pdsch_procedures_per_slot_stat[RX_NB_TH][LTE_SLOTS_PER_SUBFRAME]", &(UE->
// {"dlsch_procedures_stat[RX_NB_TH]", &(UE->
// {"dlsch_decoding_stats[2]", &(UE->
// {"dlsch_llr_stats_parallelization[RX_NB_TH][LTE_SLOTS_PER_SUBFRAME]", &(UE->
#define CPU_PHYNRUE_MEASURE \
{ \
{"phy_proc", &(UE->phy_proc[0]),0,RX_NB_TH},\
{"phy_proc_rx", &(UE-> phy_proc_rx[0]),0,RX_NB_TH},\
{"phy_proc_tx", &(UE->phy_proc_tx),0,1},\
{"ofdm_mod_stats", &(UE->ofdm_mod_stats),0,1},\
{"ulsch_encoding_stats", &(UE->ulsch_encoding_stats),0,1},\
{"ulsch_modulation_stats", &(UE->ulsch_modulation_stats),0,1},\
{"ulsch_segmentation_stats", &(UE->ulsch_segmentation_stats),0,1},\
{"ulsch_rate_matching_stats", &(UE->ulsch_rate_matching_stats),0,1},\
{"ulsch_turbo_encoding_stats", &(UE->ulsch_turbo_encoding_stats),0,1},\
{"ulsch_interleaving_stats", &(UE->ulsch_interleaving_stats),0,1},\
{"ulsch_multiplexing_stats", &(UE->ulsch_multiplexing_stats),0,1},\
{"generic_stat", &(UE->generic_stat),0,1},\
{"generic_stat_bis", &(UE->generic_stat_bis[0][0]),0,RX_NB_TH,LTE_SLOTS_PER_SUBFRAME},\
{"ofdm_demod_stats", &(UE->ofdm_demod_stats),0,1},\
{"dlsch_rx_pdcch_stats", &(UE->dlsch_rx_pdcch_stats),0,1},\
{"rx_dft_stats", &(UE->rx_dft_stats),0,1},\
{"dlsch_c...timation_stats", &(UE->dlsch_channel_estimation_stats),0,1},\
{"dlsch_f...timation_stats", &(UE->dlsch_freq_offset_estimation_stats),0,1},\
{"dlsch_demodulation_stats", &(UE->dlsch_demodulation_stats),0,1},\
{"dlsch_rate_unmatching_stats", &(UE->dlsch_rate_unmatching_stats),0,1},\
{"dlsch_turbo_decoding_stats", &(UE->dlsch_turbo_decoding_stats),0,1},\
{"dlsch_deinterleaving_stats", &(UE->dlsch_deinterleaving_stats),0,1},\
{"dlsch_llr_stats", &(UE->dlsch_llr_stats),0,1},\
{"dlsch_unscrambling_stats", &(UE->dlsch_unscrambling_stats),0,1},\
{"dlsch_rate_matching_stats", &(UE->dlsch_rate_matching_stats),0,1},\
{"dlsch_turbo_encoding_stats", &(UE->dlsch_turbo_encoding_stats),0,1},\
{"dlsch_interleaving_stats", &(UE->dlsch_interleaving_stats),0,1},\
{"dlsch_tc_init_stats", &(UE->dlsch_tc_init_stats),0,1},\
{"dlsch_tc_alpha_stats", &(UE->dlsch_tc_alpha_stats),0,1},\
{"dlsch_tc_beta_stats", &(UE->dlsch_tc_beta_stats),0,1},\
{"dlsch_tc_gamma_stats", &(UE->dlsch_tc_gamma_stats),0,1},\
{"dlsch_tc_ext_stats", &(UE->dlsch_tc_ext_stats),0,1},\
{"dlsch_tc_intl1_stats", &(UE->dlsch_tc_intl1_stats),0,1},\
{"dlsch_tc_intl2_stats", &(UE->dlsch_tc_intl2_stats),0,1},\
{"tx_prach", &(UE->tx_prach),0,1},\
{"dlsch_encoding_SIC_stats", &(UE->dlsch_encoding_SIC_stats),0,1},\
{"dlsch_scrambling_SIC_stats", &(UE->dlsch_scrambling_SIC_stats),0,1},\
{"dlsch_modulation_SIC_stats", &(UE->dlsch_modulation_SIC_stats),0,1},\
{"dlsch...ping_unit_SIC_stats", &(UE->dlsch_llr_stripping_unit_SIC_stats),0,1},\
{"dlsch_unscrambling_SIC_stats", &(UE->dlsch_unscrambling_SIC_stats),0,1},\
}
#endif #endif
...@@ -66,9 +66,10 @@ void measurcmd_display_groups(telnet_printfunc_t prnt,telnet_measurgroupdef_t *m ...@@ -66,9 +66,10 @@ void measurcmd_display_groups(telnet_printfunc_t prnt,telnet_measurgroupdef_t *m
/* cpu measurements functions */ /* cpu measurements functions */
void measurcmd_display_cpumeasures(telnet_printfunc_t prnt, telnet_cpumeasurdef_t *cpumeasure, int cpumeasure_size) { void measurcmd_display_cpumeasures(telnet_printfunc_t prnt, telnet_cpumeasurdef_t *cpumeasure, int cpumeasure_size) {
for (int i=0; i<cpumeasure_size; i++) { for (int i=0; i<cpumeasure_size; i++) {
prnt("%02d %*s: %15.3f us; %15d %s",i,TELNET_MAXMEASURNAME_LEN-1,(cpumeasure+i)->statname, for (int o1=0;o1<(cpumeasure+i)->num_occur1;o1++)
((cpumeasure+i)->astatptr->trials!=0)?(((cpumeasure+i)->astatptr->diff)/((cpumeasure+i)->astatptr->trials))/cpufreq/1000:0, prnt("%02d %*s[%i]: %15.3f us; %15d %s",i,TELNET_MAXMEASURNAME_LEN-1,(cpumeasure+i)->statname,o1,
(cpumeasure+i)->astatptr->trials, ((i%2)==1)?"|\n":" | " ); ((cpumeasure+i+o1)->astatptr->trials!=0)?(((cpumeasure+i+o1)->astatptr->diff)/((cpumeasure+i+o1)->astatptr->trials))/cpufreq/1000:0,
(cpumeasure+i+o1)->astatptr->trials, ((i%2)==1)?"|\n":" | " );
} }
prnt("\n\n"); prnt("\n\n");
......
...@@ -49,6 +49,9 @@ typedef struct cpumeasurdef { ...@@ -49,6 +49,9 @@ typedef struct cpumeasurdef {
char statname[TELNET_MAXMEASURNAME_LEN]; char statname[TELNET_MAXMEASURNAME_LEN];
time_stats_t *astatptr; time_stats_t *astatptr;
unsigned int statemask; unsigned int statemask;
uint8_t num_occur1;
uint8_t num_occur2;
uint8_t num_occur3;
} telnet_cpumeasurdef_t; } telnet_cpumeasurdef_t;
typedef struct ltemeasurdef { typedef struct ltemeasurdef {
......
...@@ -129,18 +129,14 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue, ...@@ -129,18 +129,14 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue,
rxdata_ptr = (int16_t *)tmp_dft_in; rxdata_ptr = (int16_t *)tmp_dft_in;
} }
#if UE_TIMING_TRACE
start_meas(&ue->rx_dft_stats); start_meas(&ue->rx_dft_stats);
#endif
dft(dftsize, dft(dftsize,
rxdata_ptr, rxdata_ptr,
(int16_t *)&common_vars->common_vars_rx_data_per_thread[proc->thread_id].rxdataF[aa][frame_parms->ofdm_symbol_size*symbol], (int16_t *)&common_vars->common_vars_rx_data_per_thread[proc->thread_id].rxdataF[aa][frame_parms->ofdm_symbol_size*symbol],
1); 1);
#if UE_TIMING_TRACE
stop_meas(&ue->rx_dft_stats); stop_meas(&ue->rx_dft_stats);
#endif
int symb_offset = (Ns%frame_parms->slots_per_subframe)*frame_parms->symbols_per_slot; int symb_offset = (Ns%frame_parms->slots_per_subframe)*frame_parms->symbols_per_slot;
int32_t rot2 = ((uint32_t*)frame_parms->symbol_rotation[0])[symbol+symb_offset]; int32_t rot2 = ((uint32_t*)frame_parms->symbol_rotation[0])[symbol+symb_offset];
...@@ -249,18 +245,14 @@ int nr_slot_fep_init_sync(PHY_VARS_NR_UE *ue, ...@@ -249,18 +245,14 @@ int nr_slot_fep_init_sync(PHY_VARS_NR_UE *ue,
} }
#if UE_TIMING_TRACE
start_meas(&ue->rx_dft_stats); start_meas(&ue->rx_dft_stats);
#endif
dft(dftsize, dft(dftsize,
rxdata_ptr, rxdata_ptr,
(int16_t *)&common_vars->common_vars_rx_data_per_thread[proc->thread_id].rxdataF[aa][frame_parms->ofdm_symbol_size*symbol], (int16_t *)&common_vars->common_vars_rx_data_per_thread[proc->thread_id].rxdataF[aa][frame_parms->ofdm_symbol_size*symbol],
1); 1);
#if UE_TIMING_TRACE
stop_meas(&ue->rx_dft_stats); stop_meas(&ue->rx_dft_stats);
#endif
int symb_offset = (Ns%frame_parms->slots_per_subframe)*frame_parms->symbols_per_slot; int symb_offset = (Ns%frame_parms->slots_per_subframe)*frame_parms->symbols_per_slot;
int32_t rot2 = ((uint32_t*)frame_parms->symbol_rotation[0])[symbol + symb_offset]; int32_t rot2 = ((uint32_t*)frame_parms->symbol_rotation[0])[symbol + symb_offset];
......
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