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
alex037yang
OpenXG-RAN
Commits
6cd3e5fa
Commit
6cd3e5fa
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
c73c0e3f
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 @
6cd3e5fa
...
...
@@ -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 @
6cd3e5fa
...
...
@@ -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 @
6cd3e5fa
...
...
@@ -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 @
6cd3e5fa
...
...
@@ -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 @
6cd3e5fa
File added
openair1/SIMULATION/NR_UE_PHY/unit_tests/src/dummy_functions.c
View file @
6cd3e5fa
...
...
@@ -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 @
6cd3e5fa
...
...
@@ -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 @
6cd3e5fa
#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