Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
OpenXG UE
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
Michael Black
OpenXG UE
Commits
644b8d36
Commit
644b8d36
authored
Jan 17, 2019
by
tuntt3
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix bugs related to using lte_gold_generic to generate uci
parent
41b08680
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
254 additions
and
37 deletions
+254
-37
openair1/PHY/INIT/nr_parms.c
openair1/PHY/INIT/nr_parms.c
+1
-1
openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c
openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c
+69
-30
openair1/PHY/impl_defs_nr.h
openair1/PHY/impl_defs_nr.h
+1
-1
openair1/SIMULATION/NR_UE_PHY/unit_tests/build/CMakeLists.txt
...air1/SIMULATION/NR_UE_PHY/unit_tests/build/CMakeLists.txt
+5
-0
openair1/SIMULATION/NR_UE_PHY/unit_tests/build/pucch_uci_generator_test
...ATION/NR_UE_PHY/unit_tests/build/pucch_uci_generator_test
+0
-0
openair1/SIMULATION/NR_UE_PHY/unit_tests/src/dummy_functions.c
...ir1/SIMULATION/NR_UE_PHY/unit_tests/src/dummy_functions.c
+2
-3
openair1/SIMULATION/NR_UE_PHY/unit_tests/src/pss_util_test.c
openair1/SIMULATION/NR_UE_PHY/unit_tests/src/pss_util_test.c
+5
-2
openair1/SIMULATION/NR_UE_PHY/unit_tests/src/pucch_uci_generator_test.c
...ATION/NR_UE_PHY/unit_tests/src/pucch_uci_generator_test.c
+171
-0
No files found.
openair1/PHY/INIT/nr_parms.c
View file @
644b8d36
...
...
@@ -37,7 +37,7 @@ int nr_init_frame_parms0(NR_DL_FRAME_PARMS *fp,
#if DISABLE_LOG_X
printf
(
"Initializing frame parms for mu %d, N_RB %d, Ncp %d
\n
"
,
mu
,
N_RB_DL
,
Ncp
);
#else
LOG_I
(
PHY
,
"Initializing frame parms for mu %d, N_RB %d, Ncp %d
\n
"
,
mu
,
N_RB_DL
,
Ncp
);
//
LOG_I(PHY,"Initializing frame parms for mu %d, N_RB %d, Ncp %d\n",mu, N_RB_DL, Ncp);
#endif
if
(
Ncp
==
NFAPI_CP_EXTENDED
)
...
...
openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c
View file @
644b8d36
...
...
@@ -89,6 +89,9 @@ void nr_group_sequence_hopping (//pucch_GroupHopping_t ue->pucch_config_common_n
*
v
=
0
;
uint32_t
c_init
=
(
1
<<
5
)
*
floor
(
n_id
/
30
)
+
(
n_id
%
30
);
// we initialize c_init to calculate u,v
uint32_t
x1
,
s
=
lte_gold_generic
(
&
x1
,
&
c_init
,
1
);
// TS 38.211 Subclause 5.2.1
int
l
=
32
,
minShift
=
((
2
*
nr_tti_tx
+
n_hop
)
<<
3
);
int
tmpShift
=
0
;
#ifdef DEBUG_NR_PUCCH_TX
printf
(
"
\t\t
[nr_group_sequence_hopping] calculating u,v -> "
);
#endif
...
...
@@ -97,15 +100,35 @@ void nr_group_sequence_hopping (//pucch_GroupHopping_t ue->pucch_config_common_n
f_ss
=
n_id
%
30
;
}
if
(
PUCCH_GroupHopping
==
enable
){
// PUCCH_GroupHopping 'enabled'
for
(
int
m
=
0
;
m
<
8
;
m
++
){
for
(
int
m
=
0
;
m
<
8
;
m
++
){
while
(
minShift
>=
l
){
s
=
lte_gold_generic
(
&
x1
,
&
c_init
,
0
);
l
=
l
+
32
;
}
tmpShift
=
(
minShift
&
((
1
<<
5
)
-
1
));
//minShift%32;
f_gh
=
f_gh
+
((
1
<<
m
)
*
((
uint8_t
)((
s
>>
tmpShift
)
&
1
)));
minShift
++
;
}
f_gh
=
f_gh
%
30
;
f_ss
=
n_id
%
30
;
/* for (int m=0; m<8; m++){
f_gh = f_gh + ((1<<m)*((uint8_t)((s>>(8*(2*nr_tti_tx+n_hop)+m))&1))); // Not sure we have to use nr_tti_tx FIXME!!!
}
f_gh = f_gh%30;
f_ss
=
n_id
%
30
;
f_ss = n_id%30;
*/
}
if
(
PUCCH_GroupHopping
==
disable
){
// PUCCH_GroupHopping 'disabled'
f_ss
=
n_id
%
30
;
*
v
=
(
uint8_t
)((
s
>>
(
2
*
nr_tti_tx
+
n_hop
))
&
1
);
// Not sure we have to use nr_tti_tx FIXME!!!
l
=
32
,
minShift
=
(
2
*
nr_tti_tx
+
n_hop
);
while
(
minShift
>=
l
){
s
=
lte_gold_generic
(
&
x1
,
&
c_init
,
0
);
l
=
l
+
32
;
}
tmpShift
=
(
minShift
&
((
1
<<
5
)
-
1
));
//minShift%32;
*
v
=
(
uint8_t
)((
s
>>
tmpShift
)
&
1
);
// *v = (uint8_t)((s>>(2*nr_tti_tx+n_hop))&1); // Not sure we have to use nr_tti_tx FIXME!!!
}
*
u
=
(
f_gh
+
f_ss
)
%
30
;
#ifdef DEBUG_NR_PUCCH_TX
...
...
@@ -129,7 +152,7 @@ double nr_cyclic_shift_hopping(PHY_VARS_NR_UE *ue,
*/
// alpha_init initialized to 2*PI/12=0.5235987756
double
alpha
=
0
.
5235987756
;
uint
16_t
c_init
=
ue
->
pucch_config_common_nr
->
hoppingId
;
// we initialize c_init again to calculate n_cs
uint
32_t
c_init
=
ue
->
pucch_config_common_nr
->
hoppingId
;
// uint16_t causes error in generating gold sequence later
#ifdef DEBUG_NR_PUCCH_TX
// initialization to be removed
...
...
@@ -139,12 +162,23 @@ double nr_cyclic_shift_hopping(PHY_VARS_NR_UE *ue,
uint32_t
x1
,
s
=
lte_gold_generic
(
&
x1
,
&
c_init
,
1
);
// TS 38.211 Subclause 5.2.1
uint8_t
n_cs
=
0
;
#ifdef DEBUG_NR_PUCCH_TX
printf
(
"
\t\t
[nr_cyclic_shift_hopping] calculating alpha (cyclic shift) using c_init=%d -> "
,
c_init
);
#endif
int
l
=
32
,
minShift
=
(
14
*
8
*
nr_tti_tx
)
+
8
*
(
lnormal
+
lprime
);
int
tmpShift
=
0
;
#ifdef DEBUG_NR_PUCCH_TX
printf
(
"
\t\t
[nr_cyclic_shift_hopping] calculating alpha (cyclic shift) using c_init=%d ->
\n
"
,
c_init
);
#endif
for
(
int
m
=
0
;
m
<
8
;
m
++
){
while
(
minShift
>=
l
){
s
=
lte_gold_generic
(
&
x1
,
&
c_init
,
0
);
l
=
l
+
32
;
}
tmpShift
=
(
minShift
&
((
1
<<
5
)
-
1
));
//minShift%32;
minShift
++
;
n_cs
=
n_cs
+
((
1
<<
m
)
*
((
uint8_t
)((
s
>>
tmpShift
)
&
1
)));
// calculating n_cs (Not sure we have to use nr_tti_tx FIXME!!!)
n_cs
=
n_cs
+
((
1
<<
m
)
*
((
uint8_t
)((
s
>>
((
14
*
8
*
nr_tti_tx
)
+
8
*
(
lnormal
+
lprime
)
+
m
))
&
1
)));
//
n_cs = n_cs+((1<<m)*((uint8_t)((s>>((14*8*nr_tti_tx) + 8*(lnormal+lprime) + m))&1)));
}
alpha
=
(
alpha
*
(
double
)((
m0
+
mcs
+
n_cs
)
%
12
));
#ifdef DEBUG_NR_PUCCH_TX
...
...
@@ -861,26 +895,26 @@ void nr_generate_pucch1_old(PHY_VARS_NR_UE *ue,
inline
void
nr_pucch2_3_4_scrambling
(
uint16_t
M_bit
,
uint16_t
rnti
,
uint16_t
n_id
,
uint32_t
B
,
uint8_t
*
btilde
)
__attribute__
((
always_inline
));
inline
void
nr_pucch2_3_4_scrambling
(
uint16_t
M_bit
,
uint16_t
rnti
,
uint16_t
n_id
,
uint32_t
B
,
uint8_t
*
btilde
)
{
uint32_t
x1
,
x2
,
s
=
0
;
int
i
;
uint8_t
c
;
// c_init=nRNTI*2^15+n_id according to TS 38.211 Subclause 6.3.2.6.1
//x2 = (rnti) + ((uint32_t)(1+nr_tti_tx)<<16)*(1+(fp->Nid_cell<<1));
x2
=
((
rnti
)
<<
15
)
+
n_id
;
s
=
lte_gold_generic
(
&
x1
,
&
x2
,
1
);
#ifdef DEBUG_NR_PUCCH_TX
printf
(
"
\t\t
[nr_pucch2_3_4_scrambling] gold sequence s=%lx
\n
"
,
s
);
#endif
for
(
i
=
0
;
i
<
M_bit
;
i
++
)
{
c
=
(
uint8_t
)((
s
>>
i
)
&
1
);
btilde
[
i
]
=
(((
B
>>
i
)
&
1
)
^
c
);
#ifdef DEBUG_NR_PUCCH_TX
//printf("\t\t\t btilde[%d]=%lx from scrambled bit %d\n",i,btilde[i],((B>>i)&1));
#endif
}
#ifdef DEBUG_NR_PUCCH_TX
printf
(
"
\t\t
[nr_pucch2_3_4_scrambling] scrambling M_bit=%d bits
\n
"
,
M_bit
);
#endif
uint32_t
x1
,
x2
,
s
=
0
;
int
i
;
uint8_t
c
;
// c_init=nRNTI*2^15+n_id according to TS 38.211 Subclause 6.3.2.6.1
//x2 = (rnti) + ((uint32_t)(1+nr_tti_tx)<<16)*(1+(fp->Nid_cell<<1));
x2
=
((
rnti
)
<<
15
)
+
n_id
;
s
=
lte_gold_generic
(
&
x1
,
&
x2
,
1
);
#ifdef DEBUG_NR_PUCCH_TX
printf
(
"
\t\t
[nr_pucch2_3_4_scrambling] gold sequence s=%lx
\n
"
,
s
);
#endif
for
(
i
=
0
;
i
<
M_bit
;
i
++
)
{
c
=
(
uint8_t
)((
s
>>
i
)
&
1
);
btilde
[
i
]
=
(((
B
>>
i
)
&
1
)
^
c
);
#ifdef DEBUG_NR_PUCCH_TX
//printf("\t\t\t btilde[%d]=%lx from scrambled bit %d\n",i,btilde[i],((B>>i)&1));
#endif
}
#ifdef DEBUG_NR_PUCCH_TX
printf
(
"
\t\t
[nr_pucch2_3_4_scrambling] scrambling M_bit=%d bits
\n
"
,
M_bit
);
#endif
}
void
nr_uci_encoding
(
uint64_t
payload
,
...
...
@@ -1013,7 +1047,7 @@ void nr_generate_pucch2(PHY_VARS_NR_UE *ue,
uint8_t
*
btilde
=
malloc
(
sizeof
(
int8_t
)
*
M_bit
);
// rnti is given by the C-RNTI
uint16_t
rnti
=
crnti
,
n_id
=
0
;
uint16_t
rnti
=
crnti
,
n_id
=
100
0
;
#ifdef DEBUG_NR_PUCCH_TX
printf
(
"
\t
[nr_generate_pucch2] rnti = %d ,
\n
"
,
rnti
);
#endif
...
...
@@ -1065,10 +1099,11 @@ void nr_generate_pucch2(PHY_VARS_NR_UE *ue,
uint32_t
re_offset
;
uint32_t
x1
,
x2
,
s
=
0
;
int
i
=
0
;
int
m
=
0
;
int
m
;
for
(
int
l
=
0
;
l
<
nrofSymbols
;
l
++
)
{
x2
=
(((
1
<<
17
)
*
((
14
*
nr_tti_tx
)
+
(
l
+
startingSymbolIndex
)
+
1
)
*
((
2
*
n_id
)
+
1
))
+
(
2
*
n_id
))
%
(
1
<<
31
);
// c_init calculation according to TS38.211 subclause
s
=
lte_gold_generic
(
&
x1
,
&
x2
,
1
);
m
=
0
;
for
(
int
rb
=
0
;
rb
<
nrofPRB
;
rb
++
){
//startingPRB = startingPRB + rb;
if
(((
rb
+
startingPRB
)
<
(
frame_parms
->
N_RB_DL
>>
1
))
&&
((
frame_parms
->
N_RB_DL
&
1
)
==
0
))
{
// if number RBs in bandwidth is even and current PRB is lower band
...
...
@@ -1118,6 +1153,10 @@ void nr_generate_pucch2(PHY_VARS_NR_UE *ue,
re_offset
++
;
}
i
+=
8
;
if
((
m
&
((
1
<<
4
)
-
1
))
==
0
){
s
=
lte_gold_generic
(
&
x1
,
&
x2
,
0
);
m
=
0
;
}
}
}
}
...
...
openair1/PHY/impl_defs_nr.h
View file @
644b8d36
...
...
@@ -736,7 +736,7 @@ typedef struct {
-- Corresponds to L1 parameter 'HoppingID' (see 38.211, section 6.3.2.2)
hoppingId BIT STRING (SIZE (10)) OPTIONAL, -- Need R
*/
uint
16
_t
hoppingId
;
uint
32
_t
hoppingId
;
/*
-- Power control parameter P0 for PUCCH transmissions. Value in dBm. Only even values (step size 2) allowed.
-- Corresponds to L1 parameter 'p0-nominal-pucch' (see 38.213, section 7.2)
...
...
openair1/SIMULATION/NR_UE_PHY/unit_tests/build/CMakeLists.txt
View file @
644b8d36
...
...
@@ -96,3 +96,8 @@ target_link_libraries(pucch_uci_test
-Wl,--start-group UTIL SCHED_NR_UE_LIB PHY PHY_COMMON PHY_UE PHY_NR_UE -Wl,--end-group
pthread m
${
ATLAS_LIBRARIES
}
)
add_executable
(
pucch_uci_generator_test
${
OPENAIR1_DIR
}
/SIMULATION/NR_UE_PHY/unit_tests/src/pucch_uci_generator_test.c
${
SRC_UNIT_TESTS
}
)
target_link_libraries
(
pucch_uci_generator_test
-Wl,--start-group UTIL SCHED_NR_UE_LIB PHY PHY_COMMON PHY_UE PHY_NR_UE -Wl,--end-group
pthread m
${
ATLAS_LIBRARIES
}
)
\ No newline at end of file
openair1/SIMULATION/NR_UE_PHY/unit_tests/build/pucch_uci_generator_test
0 → 100755
View file @
644b8d36
File added
openair1/SIMULATION/NR_UE_PHY/unit_tests/src/dummy_functions.c
View file @
644b8d36
...
...
@@ -192,11 +192,10 @@ int load_module_shlib(char *modname,loader_shlibfunc_t *farray, int numf) { retu
void
*
get_shlibmodule_fptr
(
char
*
modname
,
char
*
fname
)
{
return
(
NULL
)
;
}
void
exit_fun
(
const
char
*
s
)
{
/*void exit_fun (const char *s) {
VOID_PARAMETER s;
undefined_function(__FUNCTION__);
}
}
*/
uint32_t
ue_get_SR
(
module_id_t
module_idP
,
int
CC_id
,
frame_t
frameP
,
uint8_t
eNB_id
,
rnti_t
rnti
,
sub_frame_t
subframe
){
...
...
openair1/SIMULATION/NR_UE_PHY/unit_tests/src/pss_util_test.c
View file @
644b8d36
...
...
@@ -227,7 +227,7 @@ int init_test(unsigned char N_tx, unsigned char N_rx, unsigned char transmission
cpuf
=
get_cpu_freq_GHz
();
LOG_I
(
PHY
,
"[CONFIG] Test of UE synchronisation
\n
"
);
//
LOG_I(PHY, "[CONFIG] Test of UE synchronisation \n");
set_component_filelog
(
USIM
);
// file located in /tmp/testSynchroue.txt
...
...
@@ -258,7 +258,10 @@ int init_test(unsigned char N_tx, unsigned char N_rx, unsigned char transmission
frame_parms
->
threequarter_fs
=
0
;
frame_parms
->
numerology_index
=
NUMEROLOGY_INDEX_MAX_NR
;
nr_init_frame_parms_ue
(
frame_parms
);
int
mu
=
1
;
int
n_ssb_crb
=
0
;
int
ssb_subcarrier_offset
=
0
;
nr_init_frame_parms_ue
(
frame_parms
,
mu
,
extended_prefix_flag
,
N_RB_DL
,
n_ssb_crb
,
ssb_subcarrier_offset
);
PHY_vars_UE
->
frame_parms
.
Nid_cell
=
(
3
*
N_ID_1_NUMBER
)
+
N_ID_2_NUMBER
;
/* set to unvalid value */
...
...
openair1/SIMULATION/NR_UE_PHY/unit_tests/src/pucch_uci_generator_test.c
0 → 100644
View file @
644b8d36
#include "../../unit_tests/src/pss_util_test.h"
#include "PHY/defs_nr_UE.h"
#include "PHY/INIT/init_extern.h"
#include "PHY/phy_extern_nr_ue.h"
/*
#include "SCHED_NR_UE/defs.h"
#include "SCHED_NR/extern.h"
#include "SCHED_NR_UE/harq_nr.h"
*/
#include "SCHED_NR_UE/pucch_uci_ue_nr.h"
/**************** define **************************************/
#define TST_GNB_ID_0 (0)
/* first index of gNB */
#define TST_THREAD_ID (0)
int
test_pucch_generators
(
PHY_VARS_NR_UE
*
ue
)
{
int
gNB_id
=
TST_GNB_ID_0
;
int
thread_number
=
TST_THREAD_ID
;
int
TB_identifier
=
0
;
int
v_return
=
0
;
pucch_format_nr_t
format
=
pucch_format2_nr
;
uint8_t
starting_symbol_index
;
uint8_t
nb_symbols_total
=
4
;
uint16_t
starting_prb
=
0
;;
/* it can be considered as first hop on case of pucch hopping */
uint16_t
second_hop
=
0
;
/* second part for pucch for hopping */
uint8_t
nb_of_prbs
=
1
;
switch
(
format
){
case
pucch_format0_nr
:
nb_symbols_total
=
2
;
nb_of_prbs
=
1
;
starting_symbol_index
=
0
;
break
;
case
pucch_format1_nr
:
nb_symbols_total
=
5
;
nb_of_prbs
=
1
;
starting_symbol_index
=
0
;
break
;
case
pucch_format2_nr
:
nb_symbols_total
=
2
;
nb_of_prbs
=
16
;
starting_symbol_index
=
0
;
break
;
}
int
m_0
=
0
;
/* format 0 only */
int
m_CS
=
0
;
/* for all format except for format 0 */
int
index_additional_dmrs
=
I_PUCCH_NO_ADDITIONAL_DMRS
;
int
index_hopping
=
I_PUCCH_NO_HOPPING
;
int
time_domain_occ
=
0
;
int
occ_length
=
0
;
int
occ_Index
=
0
;
uint64_t
pucch_payload
=
0
;
int
tx_amp
=
512
;
int
nr_tti_tx
=
0
;
int
N_UCI
=
0
;
/* size in bits for Uplink Control Information */
switch
(
format
)
{
case
pucch_format0_nr
:
{
nr_generate_pucch0
(
ue
,
ue
->
common_vars
.
txdataF
,
&
ue
->
frame_parms
,
&
ue
->
pucch_config_dedicated_nr
[
gNB_id
],
tx_amp
,
nr_tti_tx
,
(
uint8_t
)
m_0
,
(
uint8_t
)
m_CS
,
nb_symbols_total
,
starting_symbol_index
,
starting_prb
);
break
;
}
case
pucch_format1_nr
:
{
nr_generate_pucch1
(
ue
,
ue
->
common_vars
.
txdataF
,
&
ue
->
frame_parms
,
&
ue
->
pucch_config_dedicated_nr
[
gNB_id
],
pucch_payload
,
tx_amp
,
nr_tti_tx
,
(
uint8_t
)
m_0
,
nb_symbols_total
,
starting_symbol_index
,
starting_prb
,
second_hop
,
(
uint8_t
)
time_domain_occ
,
(
uint8_t
)
N_UCI
);
break
;
}
case
pucch_format2_nr
:
{
nr_generate_pucch2
(
ue
,
ue
->
pdcch_vars
[
ue
->
current_thread_id
[
nr_tti_tx
]][
gNB_id
]
->
crnti
,
ue
->
common_vars
.
txdataF
,
&
ue
->
frame_parms
,
&
ue
->
pucch_config_dedicated_nr
[
gNB_id
],
pucch_payload
,
tx_amp
,
nr_tti_tx
,
nb_symbols_total
,
starting_symbol_index
,
nb_of_prbs
,
starting_prb
,
(
uint8_t
)
N_UCI
);
break
;
}
case
pucch_format3_nr
:
case
pucch_format4_nr
:
{
nr_generate_pucch3_4
(
ue
,
ue
->
pdcch_vars
[
ue
->
current_thread_id
[
nr_tti_tx
]][
gNB_id
]
->
crnti
,
ue
->
common_vars
.
txdataF
,
&
ue
->
frame_parms
,
format
,
&
ue
->
pucch_config_dedicated_nr
[
gNB_id
],
pucch_payload
,
tx_amp
,
nr_tti_tx
,
nb_symbols_total
,
starting_symbol_index
,
nb_of_prbs
,
starting_prb
,
second_hop
,
(
uint8_t
)
N_UCI
,
(
uint8_t
)
occ_length
,
(
uint8_t
)
occ_Index
);
break
;
}
}
return
(
v_return
);
}
int
main
(
int
argc
,
char
*
argv
[])
{
uint8_t
transmission_mode
=
1
;
uint8_t
nb_antennas_tx
=
1
;
uint8_t
nb_antennas_rx
=
1
;
uint8_t
frame_type
=
FDD
;
uint8_t
N_RB_DL
=
106
;
lte_prefix_type_t
extended_prefix_flag
=
NORMAL
;
int
Nid_cell
[]
=
{(
3
*
1
+
3
)};
VOID_PARAMETER
argc
;
VOID_PARAMETER
argv
;
printf
(
" PUCCH TEST
\n
"
);
printf
(
"-----------
\n
"
);
if
(
init_test
(
nb_antennas_tx
,
nb_antennas_rx
,
transmission_mode
,
extended_prefix_flag
,
frame_type
,
Nid_cell
[
0
],
N_RB_DL
)
!=
0
)
{
printf
(
"Initialisation problem for test
\n
"
);
exit
(
-
1
);;
}
if
(
test_pucch_generators
(
PHY_vars_UE
)
!=
0
)
{
printf
(
"
\n
Test PUCCH is fail
\n
"
);
}
else
{
printf
(
"
\n
Test PUCCH is pass
\n
"
);
}
free_context_synchro_nr
();
return
(
0
);
}
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