Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
OpenXG-RAN
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
zzha zzha
OpenXG-RAN
Commits
0317d672
Commit
0317d672
authored
Apr 02, 2021
by
Laurent THOMAS
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
sanitize, calibration tool kickoff
parent
de5e64c7
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
762 additions
and
41 deletions
+762
-41
cmake_targets/CMakeLists.txt
cmake_targets/CMakeLists.txt
+12
-0
cmake_targets/tools/run_enb_s1_usrp
cmake_targets/tools/run_enb_s1_usrp
+0
-13
common/utils/system.c
common/utils/system.c
+18
-8
common/utils/system.h
common/utils/system.h
+5
-3
executables/nr-softmodem.c
executables/nr-softmodem.c
+0
-1
executables/nr-softmodem.h
executables/nr-softmodem.h
+0
-4
executables/ocp-gnb.c
executables/ocp-gnb.c
+0
-3
openair1/PHY/TOOLS/calibration_scope.c
openair1/PHY/TOOLS/calibration_scope.c
+387
-0
openair1/PHY/TOOLS/calibration_scope.h
openair1/PHY/TOOLS/calibration_scope.h
+5
-0
openair1/PHY/TOOLS/calibration_test.c
openair1/PHY/TOOLS/calibration_test.c
+329
-0
openair2/GNB_APP/gnb_paramdef.h
openair2/GNB_APP/gnb_paramdef.h
+5
-5
openair2/SIMULATION/NR_RRC/itti_sim.c
openair2/SIMULATION/NR_RRC/itti_sim.c
+0
-3
openair2/UTIL/OCG/OCG.h
openair2/UTIL/OCG/OCG.h
+0
-1
targets/ARCH/COMMON/common_lib.h
targets/ARCH/COMMON/common_lib.h
+1
-0
No files found.
cmake_targets/CMakeLists.txt
View file @
0317d672
...
...
@@ -2797,6 +2797,7 @@ add_library(rfsimulator MODULE
${
OPENAIR_TARGETS
}
/ARCH/rfsimulator/simulator.c
${
OPENAIR_TARGETS
}
/ARCH/rfsimulator/apply_channelmod.c
${
OPENAIR_TARGETS
}
/ARCH/rfsimulator/new_channel_sim.c
${
OPENAIR1_DIR
}
/PHY/TOOLS/signal_energy.c
)
target_link_libraries
(
rfsimulator SIMU_COMMON
${
ATLAS_LIBRARIES
}
)
...
...
@@ -3181,6 +3182,17 @@ target_link_libraries (dlsim_tm4
pthread m rt
${
CONFIG_LIB
}
${
ATLAS_LIBRARIES
}
${
T_LIB
}
)
add_executable
(
rftest
${
OPENAIR_DIR
}
/openair1/PHY/TOOLS/calibration_test.c
${
OPENAIR_DIR
}
/openair1/PHY/TOOLS/calibration_scope.c
${
OPENAIR_DIR
}
/common/utils/system.c
${
OPENAIR_TARGETS
}
/ARCH/COMMON/common_lib.c
${
OPENAIR_DIR
}
/executables/softmodem-common.c
${
CONFIG_SOURCES
}
${
SHLIB_LOADER_SOURCES
}
)
target_link_libraries
(
rftest minimal_lib PHY_NR_COMMON pthread dl m forms
${
T_LIB
}
)
add_executable
(
polartest
${
OPENAIR1_DIR
}
/PHY/CODING/TESTBENCH/polartest.c
${
OPENAIR_DIR
}
/common/utils/backtrace.c
...
...
cmake_targets/tools/run_enb_s1_usrp
View file @
0317d672
...
...
@@ -101,7 +101,6 @@ function main()
local
-i
run_gdb
=
0
local
-i
show_stdout
=
0
local
exe_arguments
=
""
local
itti_dump_file
=
""
until
[
-z
"
$1
"
]
do
...
...
@@ -154,18 +153,6 @@ function main()
exe_arguments
=
"
$exe_arguments
--rf-config-file=
$rf_config_file
"
fi
;;
-K
|
--itti-dump-file
)
itti_dump_file
=
$2
# can omit file name if last arg on the line
if
[
"x
$itti_dump_file
"
=
"x"
]
;
then
itti_dump_file
=
"/tmp/enb_s1_usrp_itti.log"
shift
1
;
else
shift
2
;
fi
echo
"setting ITTI dump file to:
$itti_dump_file
"
exe_arguments
=
"
$exe_arguments
-K
$itti_dump_file
"
;;
-m
|
--mscgen
)
g_msc_dir
=
$2
# can omit file name if last arg on the line
...
...
common/utils/system.c
View file @
0317d672
...
...
@@ -199,15 +199,26 @@ void start_background_system(void) {
void
threadCreate
(
pthread_t
*
t
,
void
*
(
*
func
)(
void
*
),
void
*
param
,
char
*
name
,
int
affinity
,
int
priority
){
pthread_attr_t
attr
;
pthread_attr_init
(
&
attr
);
pthread_attr_setdetachstate
(
&
attr
,
PTHREAD_CREATE_DETACHED
);
pthread_attr_setinheritsched
(
&
attr
,
PTHREAD_EXPLICIT_SCHED
);
pthread_attr_setschedpolicy
(
&
attr
,
SCHED_FIFO
);
int
ret
;
AssertFatal
(
0
==
(
ret
=
pthread_attr_init
(
&
attr
)),
"ret: %d, errno: %d
\n
"
,
ret
,
errno
);
AssertFatal
(
0
==
(
ret
=
pthread_attr_setdetachstate
(
&
attr
,
PTHREAD_CREATE_DETACHED
)),
"ret: %d, errno: %d
\n
"
,
ret
,
errno
);
AssertFatal
(
0
==
(
ret
=
pthread_attr_setinheritsched
(
&
attr
,
PTHREAD_EXPLICIT_SCHED
)),
"ret: %d, errno: %d
\n
"
,
ret
,
errno
);
AssertFatal
(
0
==
(
ret
=
pthread_attr_setschedpolicy
(
&
attr
,
SCHED_OAI
)),
"ret: %d, errno: %d
\n
"
,
ret
,
errno
);
if
(
priority
<
sched_get_priority_min
(
SCHED_OAI
)
||
priority
>
sched_get_priority_max
(
SCHED_FIFO
))
{
LOG_E
(
TMR
,
"Prio not possible: %d, min is %d, max: %d, forced in the range
\n
"
,
priority
,
sched_get_priority_min
(
SCHED_OAI
),
sched_get_priority_max
(
SCHED_OAI
));
if
(
priority
<
sched_get_priority_min
(
SCHED_OAI
))
priority
=
sched_get_priority_min
(
SCHED_OAI
);
if
(
priority
>
sched_get_priority_max
(
SCHED_OAI
))
priority
=
sched_get_priority_max
(
SCHED_OAI
);
}
AssertFatal
(
priority
<=
sched_get_priority_max
(
SCHED_OAI
),
""
);
struct
sched_param
sparam
=
{
0
};
sparam
.
sched_priority
=
priority
;
pthread_attr_setschedparam
(
&
attr
,
&
sparam
);
pthread_create
(
t
,
&
attr
,
func
,
param
);
AssertFatal
(
0
==
(
ret
=
pthread_attr_setschedparam
(
&
attr
,
&
sparam
)),
"ret: %d, errno: %d
\n
"
,
ret
,
errno
);
AssertFatal
(
0
==
(
ret
=
pthread_create
(
t
,
&
attr
,
func
,
param
)),
"ret: %d, errno: %d
\n
"
,
ret
,
errno
);
pthread_setname_np
(
*
t
,
name
);
if
(
affinity
!=
-
1
)
{
...
...
@@ -216,7 +227,6 @@ void threadCreate(pthread_t* t, void * (*func)(void*), void * param, char* name,
CPU_SET
(
affinity
,
&
cpuset
);
AssertFatal
(
pthread_setaffinity_np
(
*
t
,
sizeof
(
cpu_set_t
),
&
cpuset
)
==
0
,
"Error setting processor affinity"
);
}
pthread_attr_destroy
(
&
attr
);
}
...
...
common/utils/system.h
View file @
0317d672
...
...
@@ -46,9 +46,11 @@ void set_latency_target(void);
void
configure_linux
(
void
);
void
threadCreate
(
pthread_t
*
t
,
void
*
(
*
func
)(
void
*
),
void
*
param
,
char
*
name
,
int
affinity
,
int
priority
);
#define OAI_PRIORITY_RT_LOW sched_get_priority_min(SCHED_FIFO)
#define OAI_PRIORITY_RT sched_get_priority_min(SCHED_FIFO+10)
#define OAI_PRIORITY_RT_MAX sched_get_priority_min(SCHED_FIFO+20)
#define SCHED_OAI SCHED_RR
#define OAI_PRIORITY_RT_LOW sched_get_priority_min(SCHED_OAI)
#define OAI_PRIORITY_RT sched_get_priority_min(SCHED_OAI)+1
#define OAI_PRIORITY_RT_MAX sched_get_priority_min(SCHED_OAI)+2
void
thread_top_init
(
char
*
thread_name
,
int
affinity
,
...
...
executables/nr-softmodem.c
View file @
0317d672
...
...
@@ -114,7 +114,6 @@ int32_t uplink_frequency_offset[MAX_NUM_CCs][4];
//Temp fix for inexistent NR upper layer
unsigned
char
NB_gNB_INST
=
1
;
static
char
*
itti_dump_file
=
NULL
;
int
UE_scan
=
1
;
int
UE_scan_carrier
=
0
;
...
...
executables/nr-softmodem.h
View file @
0317d672
...
...
@@ -18,13 +18,9 @@
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#define CMDLINE_PARAMS_DESC_GNB { \
{"mmapped-dma", CONFIG_HLP_DMAMAP, PARAMFLAG_BOOL, uptr:&mmapped_dma, defintval:0, TYPE_INT, 0}, \
{"single-thread-disable", CONFIG_HLP_NOSNGLT, PARAMFLAG_BOOL, iptr:&single_thread_flag, defintval:1, TYPE_INT, 0}, \
{"A" , CONFIG_HLP_TADV, 0, uptr:&timing_advance, defintval:0, TYPE_UINT, 0}, \
{"E" , CONFIG_HLP_TQFS, PARAMFLAG_BOOL, i8ptr:&threequarter_fs, defintval:0, TYPE_INT8, 0}, \
{"K" , CONFIG_HLP_ITTIL, PARAMFLAG_NOFREE, strptr:&itti_dump_file, defstrval:"/tmp/itti.dump", TYPE_STRING, 0}, \
{"m" , CONFIG_HLP_DLMCS, 0, uptr:&target_dl_mcs, defintval:0, TYPE_UINT, 0}, \
{"t" , CONFIG_HLP_ULMCS, 0, uptr:&target_ul_mcs, defintval:0, TYPE_UINT, 0}, \
{"usrp-tx-thread-config", CONFIG_HLP_USRP_THREAD, 0, iptr:&usrp_tx_thread, defstrval:0, TYPE_INT, 0}, \
{"s" , CONFIG_HLP_SNR, 0, dblptr:&snr_dB, defdblval:25, TYPE_DOUBLE, 0}, \
}
...
...
executables/ocp-gnb.c
View file @
0317d672
...
...
@@ -61,8 +61,6 @@ uint32_t do_forms=0;
unsigned
int
mmapped_dma
=
0
;
int8_t
threequarter_fs
=
0
;
uint32_t
target_dl_mcs
=
28
;
//maximum allowed mcs
uint32_t
target_ul_mcs
=
20
;
int
chain_offset
=
0
;
uint16_t
sl_ahead
=
6
;
uint16_t
sf_ahead
=
6
;
...
...
@@ -122,7 +120,6 @@ int restart_L1L2(module_id_t gnb_id) {
}
static
int
wait_for_sync
=
0
;
static
char
*
itti_dump_file
=
NULL
;
static
double
snr_dB
=
20
;
static
int
DEFBANDS
[]
=
{
7
};
static
int
DEFENBS
[]
=
{
0
};
...
...
openair1/PHY/TOOLS/calibration_scope.c
0 → 100644
View file @
0317d672
#include <stdlib.h>
#include <openair1/PHY/impl_defs_top.h>
#include "executables/softmodem-common.h"
#include "executables/nr-softmodem-common.h"
#include <forms.h>
#include <openair1/PHY/TOOLS/calibration_scope.h>
#define TPUT_WINDOW_LENGTH 100
#define ScaleZone 4
const
FL_COLOR
rx_antenna_colors
[
4
]
=
{
FL_RED
,
FL_BLUE
,
FL_GREEN
,
FL_YELLOW
};
const
FL_COLOR
water_colors
[
4
]
=
{
FL_BLUE
,
FL_GREEN
,
FL_YELLOW
,
FL_RED
};
typedef
struct
{
int16_t
r
;
int16_t
i
;
}
scopeSample_t
;
#define SquaredNorm(VaR) ((VaR).r*(VaR).r+(VaR).i*(VaR).i)
typedef
struct
{
void
**
samplesRx
;
openair0_device
*
rfdevice
;
}
calibData_t
;
typedef
struct
OAIgraph
{
FL_OBJECT
*
graph
;
FL_OBJECT
*
text
;
float
maxX
;
float
maxY
;
float
minX
;
float
minY
;
int
x
;
int
y
;
int
w
;
int
h
;
int
waterFallh
;
double
*
waterFallAvg
;
boolean_t
initDone
;
int
iteration
;
void
(
*
funct
)
(
struct
OAIgraph
*
graph
,
calibData_t
*
);
}
OAIgraph_t
;
/* Forms and Objects */
typedef
struct
{
calibData_t
*
context
;
FL_FORM
*
phy_scope
;
OAIgraph_t
graph
[
20
];
FL_OBJECT
*
button_0
;
}
OAI_phy_scope_t
;
typedef
struct
{
FL_FORM
*
stats_form
;
void
*
vdata
;
char
*
cdata
;
long
ldata
;
FL_OBJECT
*
stats_text
;
FL_OBJECT
*
stats_button
;
}
FD_stats_form
;
static
void
drawsymbol
(
FL_OBJECT
*
obj
,
int
id
,
FL_POINT
*
p
,
int
n
,
int
w
,
int
h
)
{
fl_points
(
p
,
n
,
FL_YELLOW
);
}
#define WATERFALL 10000
static
void
commonGraph
(
OAIgraph_t
*
graph
,
int
type
,
FL_Coord
x
,
FL_Coord
y
,
FL_Coord
w
,
FL_Coord
h
,
const
char
*
label
,
FL_COLOR
pointColor
)
{
if
(
type
==
WATERFALL
)
{
graph
->
waterFallh
=
h
-
15
;
graph
->
waterFallAvg
=
malloc
(
sizeof
(
*
graph
->
waterFallAvg
)
*
graph
->
waterFallh
);
for
(
int
i
=
0
;
i
<
graph
->
waterFallh
;
i
++
)
graph
->
waterFallAvg
[
i
]
=
0
;
graph
->
graph
=
fl_add_canvas
(
FL_NORMAL_CANVAS
,
x
,
y
,
w
,
graph
->
waterFallh
,
label
);
graph
->
text
=
fl_add_text
(
FL_NORMAL_TEXT
,
x
,
y
+
graph
->
waterFallh
,
w
,
15
,
label
);
fl_set_object_lcolor
(
graph
->
text
,
FL_WHITE
);
fl_set_object_color
(
graph
->
text
,
FL_BLACK
,
FL_BLACK
);
fl_set_object_lalign
(
graph
->
text
,
FL_ALIGN_CENTER
);
}
else
{
graph
->
graph
=
fl_add_xyplot
(
type
,
x
,
y
,
w
,
h
,
label
);
fl_set_object_lcolor
(
graph
->
graph
,
FL_WHITE
);
// Label color
fl_set_object_color
(
graph
->
graph
,
FL_BLACK
,
pointColor
);
for
(
int
i
=
0
;
i
<
FL_MAX_XYPLOTOVERLAY
;
i
++
)
fl_set_xyplot_symbol
(
graph
->
graph
,
i
,
drawsymbol
);
}
graph
->
x
=
x
;
graph
->
y
=
y
;
graph
->
w
=
w
;
graph
->
h
=
h
;
graph
->
maxX
=
0
;
graph
->
maxY
=
0
;
graph
->
minX
=
0
;
graph
->
minY
=
0
;
graph
->
initDone
=
false
;
graph
->
iteration
=
0
;
}
static
OAIgraph_t
calibrationCommonGraph
(
void
(
*
funct
)
(
OAIgraph_t
*
graph
,
calibData_t
*
context
),
int
type
,
FL_Coord
x
,
FL_Coord
y
,
FL_Coord
w
,
FL_Coord
h
,
const
char
*
label
,
FL_COLOR
pointColor
)
{
OAIgraph_t
graph
;
commonGraph
(
&
graph
,
type
,
x
,
y
,
w
,
h
,
label
,
pointColor
);
graph
.
funct
=
funct
;
return
graph
;
}
static
void
setRange
(
OAIgraph_t
*
graph
,
float
minX
,
float
maxX
,
float
minY
,
float
maxY
)
{
if
(
maxX
>
graph
->
maxX
||
minX
<
graph
->
minX
||
abs
(
maxX
-
graph
->
maxX
)
>
abs
(
graph
->
maxX
)
/
2
||
abs
(
maxX
-
graph
->
maxX
)
>
abs
(
graph
->
maxX
)
/
2
)
{
graph
->
maxX
/=
2
;
graph
->
minX
/=
2
;
graph
->
maxX
=
max
(
graph
->
maxX
,
maxX
);
graph
->
minX
=
min
(
graph
->
minX
,
minX
);
fl_set_xyplot_xbounds
(
graph
->
graph
,
graph
->
minX
*
1
.
2
,
graph
->
maxX
*
1
.
2
);
}
if
(
maxY
>
graph
->
maxY
||
minY
<
graph
->
minY
||
abs
(
maxY
-
graph
->
maxY
)
>
abs
(
graph
->
maxY
)
/
2
||
abs
(
maxY
-
graph
->
maxY
)
>
abs
(
graph
->
maxY
)
/
2
)
{
graph
->
maxY
/=
2
;
graph
->
minY
/=
2
;
graph
->
maxY
=
max
(
graph
->
maxY
,
maxY
);
graph
->
minY
=
min
(
graph
->
minY
,
minY
);
fl_set_xyplot_ybounds
(
graph
->
graph
,
graph
->
minY
*
1
.
2
,
graph
->
maxY
*
1
.
2
);
}
}
static
void
oai_xygraph_getbuff
(
OAIgraph_t
*
graph
,
float
**
x
,
float
**
y
,
int
len
,
int
layer
)
{
float
*
old_x
;
float
*
old_y
;
int
old_len
=-
1
;
if
(
graph
->
iteration
>
1
)
fl_get_xyplot_data_pointer
(
graph
->
graph
,
layer
,
&
old_x
,
&
old_y
,
&
old_len
);
if
(
old_len
!=
len
)
{
LOG_W
(
HW
,
"allocating graph of %d scope
\n
"
,
len
);
float
values
[
len
];
float
time
[
len
];
// make time in case we will use it
for
(
int
i
=
0
;
i
<
len
;
i
++
)
time
[
i
]
=
values
[
i
]
=
i
;
if
(
layer
==
0
)
fl_set_xyplot_data
(
graph
->
graph
,
time
,
values
,
len
,
""
,
""
,
""
);
else
fl_add_xyplot_overlay
(
graph
->
graph
,
layer
,
time
,
values
,
len
,
rx_antenna_colors
[
layer
]);
fl_get_xyplot_data_pointer
(
graph
->
graph
,
layer
,
&
old_x
,
&
old_y
,
&
old_len
);
AssertFatal
(
old_len
==
len
,
""
);
}
*
x
=
old_x
;
*
y
=
old_y
;
}
static
void
oai_xygraph
(
OAIgraph_t
*
graph
,
float
*
x
,
float
*
y
,
int
len
,
int
layer
,
boolean_t
NoAutoScale
)
{
fl_redraw_object
(
graph
->
graph
);
if
(
NoAutoScale
&&
graph
->
iteration
%
NoAutoScale
==
0
)
{
float
maxX
=
0
,
maxY
=
0
,
minX
=
0
,
minY
=
0
;
for
(
int
k
=
0
;
k
<
len
;
k
++
)
{
maxX
=
max
(
maxX
,
x
[
k
]);
minX
=
min
(
minX
,
x
[
k
]);
maxY
=
max
(
maxY
,
y
[
k
]);
minY
=
min
(
minY
,
y
[
k
]);
}
setRange
(
graph
,
minX
-
5
,
maxX
+
5
,
minY
-
5
,
maxY
+
5
);
}
graph
->
iteration
++
;
}
static
void
genericWaterFall
(
OAIgraph_t
*
graph
,
scopeSample_t
*
values
,
const
int
datasize
,
const
int
divisions
,
const
char
*
label
)
{
if
(
values
==
NULL
)
return
;
fl_winset
(
FL_ObjWin
(
graph
->
graph
));
const
int
samplesPerPixel
=
datasize
/
graph
->
w
;
int
displayPart
=
graph
->
waterFallh
-
ScaleZone
;
int
row
=
graph
->
iteration
%
displayPart
;
double
avg
=
0
;
for
(
int
i
=
0
;
i
<
displayPart
;
i
++
)
avg
+=
graph
->
waterFallAvg
[
i
];
avg
/=
displayPart
;
graph
->
waterFallAvg
[
row
]
=
0
;
for
(
int
pix
=
0
;
pix
<
graph
->
w
;
pix
++
)
{
scopeSample_t
*
end
=
values
+
(
pix
+
1
)
*
samplesPerPixel
;
end
-=
2
;
AssertFatal
(
end
<=
values
+
datasize
,
"diff : %ld"
,
end
-
values
+
datasize
);
double
val
=
0
;
for
(
scopeSample_t
*
s
=
values
+
(
pix
)
*
samplesPerPixel
;
s
<
end
;
s
++
)
val
+=
SquaredNorm
(
*
s
);
val
/=
samplesPerPixel
;
graph
->
waterFallAvg
[
row
]
+=
val
/
graph
->
w
;
int
col
=
0
;
if
(
val
>
avg
*
2
)
col
=
1
;
if
(
val
>
avg
*
10
)
col
=
2
;
if
(
val
>
avg
*
100
)
col
=
3
;
fl_point
(
pix
,
graph
->
iteration
%
displayPart
,
water_colors
[
col
]);
}
if
(
graph
->
initDone
==
false
)
{
for
(
int
i
=
0
;
i
<
graph
->
waterFallh
;
i
++
)
for
(
int
j
=
0
;
j
<
graph
->
w
;
j
++
)
fl_point
(
j
,
i
,
FL_BLACK
);
for
(
int
i
=
1
;
i
<
divisions
;
i
++
)
for
(
int
j
=
displayPart
;
j
<
graph
->
waterFallh
;
j
++
)
fl_point
(
i
*
(
graph
->
w
/
divisions
),
j
,
FL_WHITE
);
graph
->
initDone
=
true
;
}
fl_set_object_label_f
(
graph
->
text
,
"%s, avg I/Q pow: %4.1f"
,
label
,
sqrt
(
avg
));
graph
->
iteration
++
;
}
static
void
genericPowerPerAntena
(
OAIgraph_t
*
graph
,
const
int
nb_ant
,
const
scopeSample_t
**
data
,
const
int
len
)
{
float
*
values
,
*
time
;
oai_xygraph_getbuff
(
graph
,
&
time
,
&
values
,
len
,
0
);
for
(
int
ant
=
0
;
ant
<
nb_ant
;
ant
++
)
{
if
(
data
[
ant
]
!=
NULL
)
{
for
(
int
i
=
0
;
i
<
len
;
i
++
)
{
values
[
i
]
=
SquaredNorm
(
data
[
ant
][
i
]);
}
oai_xygraph
(
graph
,
time
,
values
,
len
,
ant
,
10
);
}
}
}
static
void
gNBWaterFall
(
OAIgraph_t
*
graph
,
calibData_t
*
context
)
{
//use 1st antenna
genericWaterFall
(
graph
,
(
scopeSample_t
*
)
context
->
samplesRx
[
0
],
0
,
0
,
"X axis:one frame in time"
);
}
static
void
gNBfreqWaterFall
(
OAIgraph_t
*
graph
,
calibData_t
*
context
)
{
//use 1st antenna
genericWaterFall
(
graph
,
(
scopeSample_t
*
)
context
->
samplesRx
[
0
],
0
,
0
,
"X axis:one frame in time"
);
}
static
void
timeResponse
(
OAIgraph_t
*
graph
,
calibData_t
*
context
)
{
#if 0
const int len=2*phy_vars_gnb->frame_parms.ofdm_symbol_size;
float *values, *time;
oai_xygraph_getbuff(graph, &time, &values, len, 0);
const int ant=0; // display antenna 0 for each UE
for (int ue=0; ue<nb_UEs; ue++) {
scopeSample_t *data= (scopeSample_t *)phy_vars_gnb->pusch_vars[ue]->ul_ch_estimates_time[ant];
if (data != NULL) {
for (int i=0; i<len; i++) {
values[i] = SquaredNorm(data[i]);
}
oai_xygraph(graph,time,values, len, ue, 10);
}
}
#endif
}
static
void
puschIQ
(
OAIgraph_t
*
graph
,
calibData_t
*
context
)
{
#if 0
NR_DL_FRAME_PARMS *frame_parms=&phy_vars_gnb->frame_parms;
int sz=frame_parms->N_RB_UL*12*frame_parms->symbols_per_slot;
for (int ue=0; ue<nb_UEs; ue++) {
scopeSample_t *pusch_comp = (scopeSample_t *) phy_vars_gnb->pusch_vars[ue]->rxdataF_comp[0];
float *I, *Q;
oai_xygraph_getbuff(graph, &I, &Q, sz, ue);
if (pusch_comp) {
for (int k=0; k<sz; k++ ) {
I[k] = pusch_comp[k].r;
Q[k] = pusch_comp[k].i;
}
oai_xygraph(graph,I,Q,sz,ue,10);
}
}
#endif
}
static
OAI_phy_scope_t
*
createScopeCalibration
(
calibData_t
*
context
)
{
FL_OBJECT
*
obj
;
OAI_phy_scope_t
*
fdui
=
calloc
((
sizeof
*
fdui
),
1
);
fdui
->
context
=
context
;
// Define form
fdui
->
phy_scope
=
fl_bgn_form
(
FL_NO_BOX
,
800
,
800
);
// This the whole UI box
obj
=
fl_add_box
(
FL_BORDER_BOX
,
0
,
0
,
800
,
800
,
""
);
fl_set_object_color
(
obj
,
FL_BLACK
,
FL_WHITE
);
int
curY
=
0
,
x
,
y
,
w
,
h
;
// Received signal
fdui
->
graph
[
0
]
=
calibrationCommonGraph
(
gNBWaterFall
,
WATERFALL
,
0
,
curY
,
400
,
100
,
"Received Signal (Time-Domain, one frame)"
,
FL_RED
);
// Time-domain channel response
//fdui->graph[1] = calibrationCommonGraph( timeResponse, FL_NORMAL_XYPLOT, 410, curY, 400, 100, "SRS Frequency Response (samples, abs)", FL_RED );
fl_get_object_bbox
(
fdui
->
graph
[
0
].
graph
,
&
x
,
&
y
,
&
w
,
&
h
);
curY
+=
h
;
// Frequency-domain channel response
fdui
->
graph
[
1
]
=
calibrationCommonGraph
(
gNBfreqWaterFall
,
WATERFALL
,
0
,
curY
,
800
,
100
,
"Channel Frequency domain (RE, one frame)"
,
FL_RED
);
fl_get_object_bbox
(
fdui
->
graph
[
1
].
graph
,
&
x
,
&
y
,
&
w
,
&
h
);
curY
+=
h
+
20
;
// LLR of PUSCH
//fdui->graph[3] = calibrationCommonGraph( puschLLR, FL_POINTS_XYPLOT, 0, curY, 500, 200, "PUSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
// I/Q PUSCH comp
fdui
->
graph
[
2
]
=
calibrationCommonGraph
(
puschIQ
,
FL_POINTS_XYPLOT
,
500
,
curY
,
300
,
200
,
"PUSCH I/Q of MF Output"
,
FL_YELLOW
);
fl_get_object_bbox
(
fdui
->
graph
[
2
].
graph
,
&
x
,
&
y
,
&
w
,
&
h
);
curY
+=
h
;
//fl_get_object_bbox(fdui->graph[6].graph,&x, &y,&w, &h);
curY
+=
h
;
fdui
->
graph
[
3
].
graph
=
NULL
;
fl_end_form
(
);
fdui
->
phy_scope
->
fdui
=
fdui
;
fl_show_form
(
fdui
->
phy_scope
,
FL_PLACE_HOTSPOT
,
FL_FULLBORDER
,
"LTE UL SCOPE gNB"
);
return
fdui
;
}
void
calibrationScope
(
OAI_phy_scope_t
*
form
)
{
int
i
=
0
;
while
(
form
->
graph
[
i
].
graph
)
{
form
->
graph
[
i
].
funct
(
form
->
graph
+
i
,
form
->
context
);
i
++
;
}
//fl_check_forms();
}
static
void
*
scopeThread
(
void
*
arg
)
{
calibData_t
*
context
=
(
calibData_t
*
)
arg
;
size_t
stksize
=
0
;
pthread_attr_t
atr
;
pthread_attr_init
(
&
atr
);
pthread_attr_getstacksize
(
&
atr
,
&
stksize
);
pthread_attr_setstacksize
(
&
atr
,
32
*
1024
*
1024
);
sleep
(
3
);
// no clean interthread barriers
int
fl_argc
=
1
;
char
*
name
=
"Calibration-scope"
;
fl_initialize
(
&
fl_argc
,
&
name
,
NULL
,
0
,
0
);
OAI_phy_scope_t
*
form
=
createScopeCalibration
(
context
);
while
(
!
oai_exit
)
{
calibrationScope
(
form
);
usleep
(
99
*
1000
);
}
return
NULL
;
}
void
CalibrationInitScope
(
void
**
samplesRx
,
openair0_device
*
rfdevice
)
{
pthread_t
forms_thread
;
calibData_t
*
tmp
=
(
calibData_t
*
)
malloc
(
sizeof
(
*
tmp
));
tmp
->
samplesRx
=
samplesRx
;
tmp
->
rfdevice
=
rfdevice
;
threadCreate
(
&
forms_thread
,
scopeThread
,
(
void
*
)
tmp
,
"scope"
,
-
1
,
OAI_PRIORITY_RT_LOW
);
}
openair1/PHY/TOOLS/calibration_scope.h
0 → 100644
View file @
0317d672
#ifndef CALIB_SCOPE_H
#define CALIB_SCOPE_H
void
CalibrationInitScope
(
void
**
samplesRx
,
openair0_device
*
rfdevice
);
#endif
openair1/PHY/TOOLS/calibration_test.c
0 → 100644
View file @
0317d672
#include <stdint.h>
#include <openair1/PHY/impl_defs_top.h>
#include <targets/ARCH/COMMON/common_lib.h>
#include <executables/softmodem-common.h>
#include <openair1/PHY/TOOLS/calibration_scope.h>
volatile
int
oai_exit
=
false
;
unsigned
int
mmapped_dma
=
0
;
int
single_thread_flag
;
uint32_t
timing_advance
;
int8_t
threequarter_fs
;
int
usrp_tx_thread
;
uint64_t
downlink_frequency
[
MAX_NUM_CCs
][
4
];
int32_t
uplink_frequency_offset
[
MAX_NUM_CCs
][
4
];
int
opp_enabled
;
static
double
snr_dB
=
20
;
#include <executables/nr-softmodem.h>
int
read_recplayconfig
(
recplay_conf_t
**
recplay_conf
,
recplay_state_t
**
recplay_state
)
{
return
0
;}
void
nfapi_setmode
(
nfapi_mode_t
nfapi_mode
)
{}
void
set_taus_seed
(
unsigned
int
seed_init
){};
int
main
(
int
argc
,
char
**
argv
)
{
///static configuration for NR at the moment
if
(
load_configmodule
(
argc
,
argv
,
CONFIG_ENABLECMDLINEONLY
)
==
NULL
)
{
exit_fun
(
"[SOFTMODEM] Error, configuration module init failed
\n
"
);
}
set_softmodem_sighandler
();
setvbuf
(
stdout
,
NULL
,
_IONBF
,
0
);
setvbuf
(
stderr
,
NULL
,
_IONBF
,
0
);
logInit
();
paramdef_t
cmdline_params
[]
=
CMDLINE_PARAMS_DESC_GNB
;
CONFIG_SETRTFLAG
(
CONFIG_NOEXITONHELP
);
get_common_options
(
SOFTMODEM_GNB_BIT
);
config_process_cmdline
(
cmdline_params
,
sizeof
(
cmdline_params
)
/
sizeof
(
paramdef_t
),
NULL
);
CONFIG_CLEARRTFLAG
(
CONFIG_NOEXITONHELP
);
configure_linux
();
int
N_RB
=
50
;
int
subCarrierFreq
=
30e3
;
int
sampling_rate
=
30.72e6
;
int
DFT
=
2048
;
int
TxAdvanceInDFTSize
=
12
;
int
antennas
=
1
;
uint64_t
freq
=
3619.200e6
;
int
rxGain
=
90
;
int
txGain
=
90
;
int
filterBand
=
40e6
;
char
*
usrp_addrs
=
"type=b200"
;
openair0_config_t
openair0_cfg
=
{
//! Module ID for this configuration
.
Mod_id
=
0
,
//! device log level
.
log_level
=
0
,
//! duplexing mode
.
duplex_mode
=
0
,
//! number of downlink resource blocks
.
num_rb_dl
=
N_RB
,
//! number of samples per frame
.
samples_per_frame
=
0
,
//! the sample rate for both transmit and receive.
.
sample_rate
=
sampling_rate
,
//device is doing mmapped DMA transfers
.
mmapped_dma
=
0
,
//! offset in samples between TX and RX paths
.
tx_sample_advance
=
0
,
//! samples per packet on the fronthaul interface
.
samples_per_packet
=
1024
,
//! number of RX channels (=RX antennas)
.
rx_num_channels
=
antennas
,
//! number of TX channels (=TX antennas)
.
tx_num_channels
=
antennas
,
//! \brief Center frequency in Hz for RX.
//! index: [0..rx_num_channels[
.
rx_freq
=
{
freq
,
freq
,
freq
,
freq
},
//! \brief Center frequency in Hz for TX.
//! index: [0..rx_num_channels[ !!! see lte-ue.c:427 FIXME iterates over rx_num_channels
.
tx_freq
=
{
freq
,
freq
,
freq
,
freq
},
//! \brief memory
//! \brief Pointer to Calibration table for RX gains
.
rx_gain_calib_table
=
NULL
,
//! mode for rxgain (ExpressMIMO2)
.
rxg_mode
=
{
0
},
//! \brief Gain for RX in dB.
//! index: [0..rx_num_channels]
.
rx_gain
=
{
rxGain
,
rxGain
,
rxGain
,
rxGain
},
//! \brief Gain offset (for calibration) in dB
//! index: [0..rx_num_channels]
.
rx_gain_offset
=
{
0
},
//! gain for TX in dB
.
tx_gain
=
{
txGain
,
txGain
,
txGain
,
txGain
},
//! RX bandwidth in Hz
.
rx_bw
=
filterBand
,
//! TX bandwidth in Hz
.
tx_bw
=
filterBand
,
//! clock source
.
clock_source
=
external
,
//internal gpsdo external
//! timing_source
.
time_source
=
internal
,
//internal gpsdo external
//! Manual SDR IP address
//#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR)
.
sdr_addrs
=
usrp_addrs
,
//! Auto calibration flag
.
autocal
=
{
0
},
//! rf devices work with x bits iqs when oai have its own iq format
//! the two following parameters are used to convert iqs
.
iq_txshift
=
0
,
.
iq_rxrescale
=
0
,
//! Configuration file for LMS7002M
.
configFilename
=
""
,
//! remote IP/MAC addr for Ethernet interface
.
remote_addr
=
""
,
//! remote port number for Ethernet interface
.
remote_port
=
0
,
//! local IP/MAC addr for Ethernet interface (eNB/BBU, UE)
.
my_addr
=
0
,
//! local port number for Ethernet interface (eNB/BBU, UE)
.
my_port
=
0
,
//! record player configuration, definition in record_player.h
.
recplay_mode
=
0
,
.
recplay_conf
=
NULL
,
//! number of samples per tti
.
samples_per_tti
=
0
,
//! check for threequarter sampling rate
.
threequarter_fs
=
0
,
};
//-----------------------
openair0_device
rfdevice
=
{
/*!tx write thread*/
//.write_thread={0},
/*!brief Module ID of this device */
.
Mod_id
=
0
,
/*!brief Component Carrier ID of this device */
.
CC_id
=
0
,
/*!brief Type of this device */
.
type
=
NONE_DEV
,
/*!brief Transport protocol type that the device supports (in case I/Q samples need to be transported) */
.
transp_type
=
NONE_TP
,
/*!brief Type of the device's host (RAU/RRU) */
.
host_type
=
MIN_HOST_TYPE
,
/* !brief RF frontend parameters set by application */
.
openair0_cfg
=
NULL
,
//set by device_init
/* !brief ETH params set by application */
.
eth_params
=
NULL
,
//! record player data, definition in record_player.h
.
recplay_state
=
NULL
,
/* !brief Indicates if device already initialized */
.
is_init
=
0
,
/*!brief Can be used by driver to hold internal structure*/
.
priv
=
NULL
,
/* Functions API, which are called by the application*/
/*! \brief Called to start the transceiver. Return 0 if OK, < 0 if error
@param device pointer to the device structure specific to the RF hardware target
*/
.
trx_start_func
=
NULL
,
/*! \brief Called to configure the device
@param device pointer to the device structure specific to the RF hardware target
*/
.
trx_config_func
=
NULL
,
/*! \brief Called to send a request message between RAU-RRU on control port
@param device pointer to the device structure specific to the RF hardware target
@param msg pointer to the message structure passed between RAU-RRU
@param msg_len length of the message
*/
.
trx_ctlsend_func
=
NULL
,
/*! \brief Called to receive a reply message between RAU-RRU on control port
@param device pointer to the device structure specific to the RF hardware target
@param msg pointer to the message structure passed between RAU-RRU
@param msg_len length of the message
*/
.
trx_ctlrecv_func
=
NULL
,
/*! \brief Called to send samples to the RF target
@param device pointer to the device structure specific to the RF hardware target
@param timestamp The timestamp at whicch the first sample MUST be sent
@param buff Buffer which holds the samples (2 dimensional)
@param nsamps number of samples to be sent
@param number of antennas
@param flags flags must be set to TRUE if timestamp parameter needs to be applied
*/
.
trx_write_func
=
NULL
,
/*! \brief Called to send samples to the RF target
@param device pointer to the device structure specific to the RF hardware target
@param timestamp The timestamp at whicch the first sample MUST be sent
@param buff Buffer which holds the samples (1 dimensional)
@param nsamps number of samples to be sent
@param antenna_id index of the antenna if the device has multiple anteannas
@param flags flags must be set to TRUE if timestamp parameter needs to be applied
*/
.
trx_write_func2
=
NULL
,
/*! \brief Receive samples from hardware.
* Read \ref nsamps samples from each channel to buffers. buff[0] is the array for
* the first channel. *ptimestamp is the time at which the first sample
* was received.
* \param device the hardware to use
* \param[out] ptimestamp the time at which the first sample was received.
* \param[out] buff An array of pointers to buffers for received samples. The buffers must be large enough to hold the number of samples \ref nsamps.
* \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
* \param num_antennas number of antennas from which to receive samples
* \returns the number of sample read
*/
.
trx_read_func
=
NULL
,
/*! \brief Receive samples from hardware, this version provides a single antenna at a time and returns.
* Read \ref nsamps samples from each channel to buffers. buff[0] is the array for
* the first channel. *ptimestamp is the time at which the first sample
* was received.
* \param device the hardware to use
* \param[out] ptimestamp the time at which the first sample was received.
* \param[out] buff A pointers to a buffer for received samples. The buffer must be large enough to hold the number of samples \ref nsamps.
* \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
* \param antenna_id Index of antenna from which samples were received
* \returns the number of sample read
*/
.
trx_read_func2
=
NULL
,
/*! \brief print the device statistics
* \param device the hardware to use
* \returns 0 on success
*/
/*! \brief print the device statistics
* \param device the hardware to use
* \returns 0 on success
*/
.
trx_get_stats_func
=
NULL
,
/*! \brief Reset device statistics
* \param device the hardware to use
* \returns 0 in success
*/
.
trx_reset_stats_func
=
NULL
,
/*! \brief Terminate operation of the transceiver -- free all associated resources
* \param device the hardware to use
*/
.
trx_end_func
=
NULL
,
/*! \brief Stop operation of the transceiver
*/
.
trx_stop_func
=
NULL
,
/* Functions API related to UE*/
/*! \brief Set RX feaquencies
* \param device the hardware to use
* \param openair0_cfg RF frontend parameters set by application
* \param exmimo_dump_config dump EXMIMO configuration
* \returns 0 in success
*/
.
trx_set_freq_func
=
NULL
,
/*! \brief Set gains
* \param device the hardware to use
* \param openair0_cfg RF frontend parameters set by application
* \returns 0 in success
*/
.
trx_set_gains_func
=
NULL
,
/*! \brief RRU Configuration callback
* \param idx RU index
* \param arg pointer to capabilities or configuration
*/
.
configure_rru
=
NULL
,
/*! \brief Pointer to generic RRU private information
*/
.
thirdparty_priv
=
NULL
,
.
thirdparty_init
=
NULL
,
/*! \brief Callback for Third-party RRU Cleanup routine
\param device the hardware configuration to use
*/
.
thirdparty_cleanup
=
NULL
,
/*! \brief Callback for Third-party start streaming routine
\param device the hardware configuration to use
*/
.
thirdparty_startstreaming
=
NULL
,
/*! \brief RRU Configuration callback
* \param idx RU index
* \param arg pointer to capabilities or configuration
*/
.
trx_write_init
=
NULL
,
/* \brief Get internal parameter
* \param id parameter to get
* \return a pointer to the parameter
*/
.
get_internal_parameter
=
NULL
,
};
openair0_device_load
(
&
rfdevice
,
&
openair0_cfg
);
void
**
samplesRx
=
(
void
**
)
malloc16
(
antennas
*
sizeof
(
struct
complex16
*
)
);
void
**
samplesTx
=
(
void
**
)
malloc16
(
antennas
*
sizeof
(
struct
complex16
*
)
);
for
(
int
i
=
0
;
i
<
antennas
;
i
++
)
{
samplesRx
[
i
]
=
(
int32_t
*
)
malloc16_clear
(
DFT
*
sizeof
(
struct
complex16
)
);
samplesTx
[
i
]
=
(
int32_t
*
)
malloc16_clear
(
DFT
*
sizeof
(
struct
complex16
)
);
}
CalibrationInitScope
(
samplesRx
,
&
rfdevice
);
openair0_timestamp
timestamp
=
0
;
rfdevice
.
trx_start_func
(
&
rfdevice
);
while
(
!
oai_exit
)
{
int
readBlockSize
=
rfdevice
.
trx_read_func
(
&
rfdevice
,
&
timestamp
,
samplesTx
,
DFT
,
antennas
);
int
txs
=
rfdevice
.
trx_write_func
(
&
rfdevice
,
timestamp
+
TxAdvanceInDFTSize
*
DFT
,
samplesRx
,
DFT
,
antennas
,
0
);
}
return
0
;
}
openair2/GNB_APP/gnb_paramdef.h
View file @
0317d672
...
...
@@ -324,12 +324,12 @@ typedef enum {
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*--------------------------------------------------------------------------------------------------------------------------------------------------*/
#define GNBNETPARAMS_DESC { \
{GNB_CONFIG_STRING_GNB_INTERFACE_NAME_FOR_NG_AMF, NULL, 0, strptr:NULL, defstrval:
NULL
, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_GNB_IPV4_ADDRESS_FOR_NG_AMF, NULL, 0, strptr:NULL, defstrval:
NULL
, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_GNB_INTERFACE_NAME_FOR_NGU, NULL, 0, strptr:NULL, defstrval:
NULL
, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_GNB_IPV4_ADDR_FOR_NGU, NULL, 0, strptr:NULL, defstrval:
NULL
, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_GNB_INTERFACE_NAME_FOR_NG_AMF, NULL, 0, strptr:NULL, defstrval:
"lo"
, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_GNB_IPV4_ADDRESS_FOR_NG_AMF, NULL, 0, strptr:NULL, defstrval:
"127.0.0.1"
, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_GNB_INTERFACE_NAME_FOR_NGU, NULL, 0, strptr:NULL, defstrval:
"lo"
, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_GNB_IPV4_ADDR_FOR_NGU, NULL, 0, strptr:NULL, defstrval:
"127.0.0.1"
, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_GNB_PORT_FOR_NGU, NULL, 0, uptr:NULL, defintval:2152L, TYPE_UINT, 0}, \
{GNB_CONFIG_STRING_ENB_IPV4_ADDR_FOR_X2C, NULL, 0, strptr:NULL, defstrval:
NULL
, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_ENB_IPV4_ADDR_FOR_X2C, NULL, 0, strptr:NULL, defstrval:
"127.0.0.1"
, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_ENB_PORT_FOR_X2C, NULL, 0, uptr:NULL, defintval:0L, TYPE_UINT, 0} \
}
...
...
openair2/SIMULATION/NR_RRC/itti_sim.c
View file @
0317d672
...
...
@@ -87,8 +87,6 @@ pthread_mutex_t nfapi_sync_mutex;
int
nfapi_sync_var
=-
1
;
//!< protected by mutex \ref nfapi_sync_mutex
uint8_t
nfapi_mode
=
0
;
// Default to monolithic mode
uint32_t
target_dl_mcs
=
28
;
uint32_t
target_ul_mcs
=
20
;
uint32_t
timing_advance
=
0
;
uint64_t
num_missed_slots
=
0
;
...
...
@@ -120,7 +118,6 @@ int32_t uplink_frequency_offset[MAX_NUM_CCs][4];
//Temp fix for inexistent NR upper layer
unsigned
char
NB_gNB_INST
=
1
;
static
char
*
itti_dump_file
=
NULL
;
int
UE_scan
=
1
;
int
UE_scan_carrier
=
0
;
...
...
openair2/UTIL/OCG/OCG.h
View file @
0317d672
...
...
@@ -667,7 +667,6 @@ typedef struct {
unsigned
char
omv_enabled
;
// openair mobility visulizer
unsigned
char
opp_enabled
;
// openair performance profiler
unsigned
char
oeh_enabled
;
// openair event handler, with CLI this could provide a remote event management
char
*
itti_dump_file
;
unsigned
char
vcd_enabled
;
char
*
vcd_file
;
unsigned
char
eMBMS_active_state
;
...
...
targets/ARCH/COMMON/common_lib.h
View file @
0317d672
...
...
@@ -33,6 +33,7 @@
#ifndef COMMON_LIB_H
#define COMMON_LIB_H
#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <openair1/PHY/TOOLS/tools_defs.h>
#include "record_player.h"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment