Commit a87a38a8 authored by Deokseong "David" Kim's avatar Deokseong "David" Kim Committed by Melissa

Setup Relay UE between SyncRef UE and Nearby UE, and then forwarding packets...

Setup Relay UE between SyncRef UE and Nearby UE, and then forwarding packets from SyncRef UE to Nearby UE.
parent a606a6ff
......@@ -312,14 +312,18 @@ static void nr_phy_config_request_sl(PHY_VARS_NR_UE *ue,
ue->slss->sl_mib[i] = 0;
}
#if 1
uint16_t node_id = get_softmodem_params()->node_number;
ue->slss->sl_mib_length = 32;
ue->slss->sl_numssb_withinperiod_r16 = 2;
ue->slss->sl_timeinterval_r16 = 20;
// TODO: The following setting needs update from MAC layer later.
ue->slss->sl_timeoffsetssb_r16 = 2;
ue->slss->sl_numssb_withinperiod_r16_copy = 2;
ue->slss->sl_timeinterval_r16_copy = 20;
ue->slss->sl_timeoffsetssb_r16_copy = 2;
if (node_id == 3) {
ue->slss->sl_timeoffsetssb_r16 = 4;
}
ue->slss->sl_numssb_withinperiod_r16_copy = ue->slss->sl_numssb_withinperiod_r16;
ue->slss->sl_timeinterval_r16_copy = ue->slss->sl_timeinterval_r16;
ue->slss->sl_timeoffsetssb_r16_copy = ue->slss->sl_timeoffsetssb_r16;
#endif
ue->slss->slss_id = Nid_SL;
ue->is_synchronized_sl = 0;
......
......@@ -75,6 +75,10 @@ void free_nr_ue_dlsch(NR_UE_DLSCH_t **dlschptr, uint16_t N_RB_DL) {
free16(dlsch->harq_processes[i]->b,a_segments*1056);
dlsch->harq_processes[i]->b = NULL;
}
if (dlsch->harq_processes[i]->b_sci2) {
free16(dlsch->harq_processes[i]->b_sci2, a_segments * 1056);
dlsch->harq_processes[i]->b_sci2 = NULL;
}
for (int r=0; r<a_segments; r++) {
free16(dlsch->harq_processes[i]->c[r],1056);
......@@ -109,6 +113,7 @@ NR_UE_DLSCH_t *new_nr_ue_dlsch(uint8_t Kmimo,uint8_t Mdlharq,uint32_t Nsoft,uint
}
uint32_t dlsch_bytes = a_segments*1056; // allocated bytes per segment
uint8_t sci2_bytes = 8; // allocated bytes for SCI2
dlsch = (NR_UE_DLSCH_t *)malloc16(sizeof(NR_UE_DLSCH_t));
if (dlsch) {
......@@ -133,6 +138,12 @@ NR_UE_DLSCH_t *new_nr_ue_dlsch(uint8_t Kmimo,uint8_t Mdlharq,uint32_t Nsoft,uint
else
exit_flag=3;
dlsch->harq_processes[i]->b_sci2 = malloc16(sci2_bytes);
if (dlsch->harq_processes[i]->b_sci2)
memset(dlsch->harq_processes[i]->b_sci2, 0, sci2_bytes);
else
exit_flag=3;
dlsch->harq_processes[i]->c = (uint8_t **)malloc16(a_segments*sizeof(uint8_t *));
dlsch->harq_processes[i]->d = (int16_t **)malloc16(a_segments*sizeof(int16_t *));
for (int r=0; r<a_segments; r++) {
......
......@@ -330,6 +330,11 @@ int nr_sl_initial_sync(UE_nr_rxtx_proc_t *proc,
if (ret == 0) {
nr_gold_psbch(ue);
ret = nr_psbch_detection(proc, ue, 0, &phy_pdcch_config);
if ((proc->nr_slot_rx != ue->slss->sl_timeoffsetssb_r16) && (get_softmodem_params()->node_number)) {
LOG_I(PHY, "Filtering out the direct connection between SyncRef UE and Nearby UE when Relay UE exists.\n");
return -1;
}
}
if (ret == 0) {
// sync at symbol ue->symbol_offset
......
......@@ -331,7 +331,7 @@ int nr_rx_psbch( PHY_VARS_NR_UE *ue,
NR_UE_COMMON *nr_ue_common_vars = &ue->common_vars;
uint8_t ssb_index = 0; //TODO: Need update to get 0 or 1 from parameter in case of mu = 1.
int symbol_offset = ue->is_synchronized > 0 ? (ue->slss->sl_timeoffsetssb_r16 + ue->slss->sl_timeinterval_r16 * ssb_index) * frame_parms->symbols_per_slot : 0;
int symbol_offset = ue->is_synchronized_sl > 0 ? (ue->slss->sl_timeoffsetssb_r16 + ue->slss->sl_timeinterval_r16 * ssb_index) * frame_parms->symbols_per_slot : 0;
int psbch_e_rx_idx = 0;
int16_t psbch_unClipped[NR_POLAR_PSBCH_E] = {0};
int16_t psbch_e_rx[NR_POLAR_PSBCH_E] = {0};
......
......@@ -279,11 +279,14 @@ int nr_slsch_encoding(PHY_VARS_NR_UE *ue,
NR_POLAR_SCI2_MESSAGE_TYPE,
polar_encoder_output_len,
NR_POLAR_SCI2_AGGREGATION_LEVEL);
#if 0
slsch->Nidx = get_Nidx_from_CRC(harq_process->a_sci2, 0, 0,
NR_POLAR_SCI2_MESSAGE_TYPE,
polar_encoder_output_len,
NR_POLAR_SCI2_AGGREGATION_LEVEL);
check_Nidx_value(slsch->Nidx, frame_parms);
#endif
slsch->Nidx = 1;
harq_process->B_sci2 = polar_encoder_output_len;
byte2bit(harq_process->b_sci2, harq_process->f_sci2, polar_encoder_output_len>>3);
/*
......
......@@ -357,22 +357,21 @@ uint32_t nr_slsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
short *data_llr = dlsch_llr + harq_process->B_sci2 * harq_process->Nl;
#if 1
uint64_t tmp = 0;
int16_t decoder_input[1792] = {0}; // 1792 = harq_process->B_sci2
nr_sci2_quantize(decoder_input, sci2_llr, harq_process->B_sci2);
uint32_t decoder_state = polar_decoder_int16(decoder_input,
(uint64_t *)&tmp,
harq_process->b_sci2,
0,
NR_POLAR_SCI2_MESSAGE_TYPE,
harq_process->B_sci2,
NR_POLAR_SCI2_AGGREGATION_LEVEL);
if (decoder_state) {
LOG_E(NR_PHY, "polar_decoder_int16 failed with ret %d\n", decoder_state);
LOG_E(NR_PHY, "polar_decoder_int16 failed with ret %d for slot %d\n", decoder_state, nr_slot_rx);
return(decoder_state);
}
harq_process->b_sci2 = &tmp;
#endif
nb_rb = harq_process->nb_rb;
A = (harq_process->TBS) << 3;
ret = dlsch->max_ldpc_iterations + 1;
......
......@@ -60,6 +60,64 @@
//extern int32_t uplink_counter;
#define SCI2_LEN_SIZE 35
#define HNA_SIZE 16 * 68 * 384 // [hna] 16 segments, 68*Zc
#define MAX_RELAY_Q_SIZE 5
pthread_mutex_t relay_mem_lock;
uint8_t cirBufRelay[MAX_RELAY_Q_SIZE][HNA_SIZE];
uint64_t cirBufRelaySCI2[MAX_RELAY_Q_SIZE];
uint8_t cirBufReadInx;
uint8_t cirBufWriteInx;
uint32_t relay_data_to_send;
void init_mutex_of_relay_data()
{
pthread_mutex_init(&relay_mem_lock, NULL);
}
uint32_t qsize_of_relay_data()
{
pthread_mutex_lock(&relay_mem_lock);
uint32_t temp = relay_data_to_send;
pthread_mutex_unlock(&relay_mem_lock);
return temp;
}
void get_relay_data_from_buffer(uint8_t *dest, uint64_t *sci2, uint32_t sz)
{
pthread_mutex_lock(&relay_mem_lock);
for (int i = 0; i < sz; i++) {
dest[i] = cirBufRelay[cirBufReadInx][i];
}
*sci2 = cirBufRelaySCI2[cirBufReadInx];
cirBufReadInx = (cirBufReadInx + 1) % MAX_RELAY_Q_SIZE;
relay_data_to_send--;
LOG_D(PHY, "Decreased the number of relay_data_to_send %u\n", relay_data_to_send);
pthread_mutex_unlock(&relay_mem_lock);
}
void put_relay_data_to_buffer(uint8_t *src, uint64_t *sci2, uint32_t sz)
{
pthread_mutex_lock(&relay_mem_lock);
if(relay_data_to_send >= MAX_RELAY_Q_SIZE) {
LOG_W(PHY, "Relay Buffer overflow: relay_data_to_send %u >= %u Capacity\n", relay_data_to_send, MAX_RELAY_Q_SIZE);
}
for (int i = 0; i < sz; i++) {
cirBufRelay[cirBufWriteInx][i] = src[i];
}
cirBufRelaySCI2[cirBufWriteInx] = *sci2;
cirBufWriteInx = (cirBufWriteInx + 1) % MAX_RELAY_Q_SIZE;
relay_data_to_send++;
LOG_D(PHY, "Increased the number of relay_data_to_send %u\n", relay_data_to_send);
pthread_mutex_unlock(&relay_mem_lock);
}
void nr_pusch_codeword_scrambling_sl(uint8_t *in,
uint32_t size,
uint32_t SCI2_bits,
......@@ -312,22 +370,31 @@ void nr_ue_set_slsch(NR_DL_FRAME_PARMS *fp,
uint64_t *sci_input = harq->a_sci2;
bool payload_type_string = false;
if (payload_type_string) {
for (int i = 0; i < 32; i++) {
test_input[i] = get_softmodem_params()->sl_user_msg[i];
if(qsize_of_relay_data() == 0) {
if (payload_type_string) {
for (int i = 0; i < 32; i++) {
test_input[i] = get_softmodem_params()->sl_user_msg[i];
}
} else {
srand(time(NULL));
for (int i = 0; i < TBS / 8; i++)
test_input[i] = (unsigned char) (i+3);//rand();
test_input[0] = (unsigned char) (slot);
test_input[1] = (unsigned char) (frame & 0xFF); // 8 bits LSB
test_input[2] = (unsigned char) ((frame >> 8) & 0x3); //
test_input[3] = (unsigned char) ((frame & 0x111) << 5) + (unsigned char) (slot) + rand() % 256;
LOG_D(NR_PHY, "SLSCH_TX will send %u\n", test_input[3]);
}
uint64_t u = 0;
uint64_t dest = 0;
dest = (0x2 + ((slot % 2) == 0 ? 1 : 0)) * get_softmodem_params()->node_number;
u ^= (dest << 32);
*sci_input = u;
} else {
srand(time(NULL));
for (int i = 0; i < TBS / 8; i++)
test_input[i] = (unsigned char) (i+3);//rand();
// test_input[0] = (unsigned char) (slot);
// test_input[1] = (unsigned char) (frame & 0xFF); // 8 bits LSB
// test_input[2] = (unsigned char) ((frame >> 8) & 0x3); //
// test_input[3] = (unsigned char) ((frame & 0x111) << 5) + (unsigned char) (slot) + rand() % 256;
LOG_D(NR_PHY, "SLSCH_TX will send %u\n", test_input[3]);
get_relay_data_from_buffer(test_input, sci_input, TBS / 8);
uint64_t dest = (*sci_input >> 32) & 0xFFFF;
LOG_W(NR_PHY, "SLSCH_TX will forward with original slot index %u for dest %d\n", test_input[0], dest);
}
uint64_t u = pow(2,SCI2_LEN_SIZE) - 1;
*sci_input = u;//rand() % (u - 0 + 1);
}
void nr_ue_slsch_tx_procedures(PHY_VARS_NR_UE *txUE,
......
......@@ -1872,6 +1872,14 @@ void nr_pssch_data_control_multiplexing(uint8_t *in_slssh,
uint8_t* out);
uint32_t get_B_multiplexed_value(NR_DL_FRAME_PARMS* fp, NR_DL_UE_HARQ_t *harq);
void init_mutex_of_relay_data();
uint32_t qsize_of_relay_data();
void get_relay_data_from_buffer(uint8_t *dest, uint64_t *sci2, uint32_t sz);
void put_relay_data_to_buffer(uint8_t *src, uint64_t *sci2, uint32_t sz);
/**@}*/
#endif
......@@ -61,7 +61,7 @@
//#define NR_PUCCH_SCHED
//#define NR_PUCCH_SCHED_DEBUG
//#define NR_PDSCH_DEBUG
#define HNA_SIZE 6 * 68 * 384 // [hna] 16 segments, 68*Zc
#define HNA_SIZE 16 * 68 * 384 // [hna] 16 segments, 68*Zc
#define LDPC_MAX_LIMIT 31
#ifndef PUCCH
#define PUCCH
......@@ -283,15 +283,22 @@ void ue_ta_procedures(PHY_VARS_NR_UE *ue, int slot_tx, int frame_tx){
bool phy_ssb_slot_allocation_sl(PHY_VARS_NR_UE *ue, int frame, int slot)
{
NR_DL_FRAME_PARMS *fp = &ue->frame_parms;
static int sl_numssb_withinperiod_r16, sl_timeoffsetssb_r16, sl_timeoffsetssb_r16_copy;
if (sl_numssb_withinperiod_r16 == 0) {
sl_numssb_withinperiod_r16 = ue->slss->sl_numssb_withinperiod_r16;
sl_timeoffsetssb_r16 = ue->slss->sl_timeoffsetssb_r16;
sl_timeoffsetssb_r16_copy = sl_timeoffsetssb_r16;
}
if ((frame * fp->slots_per_frame + slot) % (16 * fp->slots_per_frame) == 0) {
ue->slss->sl_numssb_withinperiod_r16 = ue->slss->sl_numssb_withinperiod_r16_copy;
ue->slss->sl_timeoffsetssb_r16 = frame * fp->slots_per_frame + ue->slss->sl_timeoffsetssb_r16_copy;
sl_timeoffsetssb_r16 = frame * fp->slots_per_frame + sl_timeoffsetssb_r16_copy;
}
if (ue->slss->sl_numssb_withinperiod_r16 > 0) {
if (frame * fp->slots_per_frame + slot == ue->slss->sl_timeoffsetssb_r16) {
ue->slss->sl_timeoffsetssb_r16 = ue->slss->sl_timeoffsetssb_r16 + ue->slss->sl_timeinterval_r16;
if (frame * fp->slots_per_frame + slot == sl_timeoffsetssb_r16) {
sl_timeoffsetssb_r16 = sl_timeoffsetssb_r16 + ue->slss->sl_timeinterval_r16;
ue->slss->sl_numssb_withinperiod_r16 = ue->slss->sl_numssb_withinperiod_r16 - 1;
LOG_I(PHY,"*** SL-SSB slot allocation %d.%d ***\n", frame, slot);
} else {
......@@ -366,12 +373,17 @@ void phy_procedures_nrUE_SL_TX(PHY_VARS_NR_UE *ue,
hexdump((void *)&ue->common_vars.txdata[0][slot_timestamp + ue->frame_parms.ofdm_symbol_size * i], ue->frame_parms.ofdm_symbol_size, buffer0, sizeof(buffer0)));
}
#endif
}
else if (ue->sync_ref && ((slot_tx == 1) || (slot_tx == 0))) {
for (uint8_t harq_pid = 0; harq_pid < 1; harq_pid++) {
nr_ue_set_slsch(&ue->frame_parms, harq_pid, ue->slsch[proc->thread_id][gNB_id], frame_tx, slot_tx);
if (ue->slsch[proc->thread_id][gNB_id]->harq_processes[harq_pid]->status == ACTIVE) {
nr_ue_slsch_tx_procedures(ue, harq_pid, frame_tx, slot_tx);
} else {
uint32_t sl_bitmap_tx = 0x00000;
if(ue->sync_ref) {
sl_bitmap_tx = (ue->is_synchronized_sl == 0) ? 0x00001 : 0x00002; // SyncRef UE tx slot 0, Relay UE B tx slot 1.
}
if ((sl_bitmap_tx >> slot_tx) & 1) {
for (uint8_t harq_pid = 0; harq_pid < 1; harq_pid++) {
nr_ue_set_slsch(&ue->frame_parms, harq_pid, ue->slsch[proc->thread_id][gNB_id], frame_tx, slot_tx);
if (ue->slsch[proc->thread_id][gNB_id]->harq_processes[harq_pid]->status == ACTIVE) {
nr_ue_slsch_tx_procedures(ue, harq_pid, frame_tx, slot_tx);
}
}
}
}
......@@ -1426,15 +1438,19 @@ int phy_procedures_nrUE_SL_RX(PHY_VARS_NR_UE *ue,
uint8_t synchRefUE_id,
notifiedFIFO_t *txFifo) {
if (ue->sync_ref || ue->is_synchronized_sl == 0 || (proc->nr_slot_rx != 1 && proc->nr_slot_rx != 0)) {
if (ue->is_synchronized_sl == 0)
return (0);
// TODO: Need to add rx SSB slot 2 (to Relay UE) and rx SSB slot 4 (Nearby UE) for resync
uint32_t sl_bitmap_rx = ue->sync_ref ? 0x00001 : 0x00002; // Relay UE B rx slot 0, Nearby UE rx slot 1.
if (((sl_bitmap_rx >> proc->nr_slot_rx) & 1) == 0)
return (0);
}
int frame_rx = proc->frame_rx;
int slot_rx = proc->nr_slot_rx;
if ( getenv("RFSIMULATOR") != NULL ) {
if (get_softmodem_params()->sl_mode == 2) {
if ( getenv("RFSIMULATOR") != NULL ) {
if (get_softmodem_params()->sl_mode == 2) {
int frame_length_complex_samples = ue->frame_parms.samples_per_subframe * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
for (int i = 0; i < frame_length_complex_samples; i++) {
double sigma2_dB = 20 * log10((double)AMP / 4) - ue->snr;
......@@ -1445,14 +1461,18 @@ if ( getenv("RFSIMULATOR") != NULL ) {
}
}
}
}
}
NR_UE_DLSCH_t *slsch = ue->slsch_rx[proc->thread_id][synchRefUE_id][0];
NR_DL_UE_HARQ_t *harq = NULL;
int32_t **rxdataF = ue->common_vars.common_vars_rx_data_per_thread[0].rxdataF;
uint64_t rx_offset = (slot_rx&3)*(ue->frame_parms.symbols_per_slot * ue->frame_parms.ofdm_symbol_size);
bool payload_type_string = false;
static bool detect_new_dest;
uint16_t node_id = get_softmodem_params()->node_number;
uint32_t B_mul = get_B_multiplexed_value(&ue->frame_parms, slsch->harq_processes[0]);
uint16_t Nidx = 1;
for (unsigned char harq_pid = 0; harq_pid < 1; harq_pid++) {
nr_ue_set_slsch_rx(ue, harq_pid);
if (slsch->harq_processes[harq_pid]->status == ACTIVE) {
......@@ -1463,14 +1483,30 @@ if ( getenv("RFSIMULATOR") != NULL ) {
}
apply_nr_rotation_ul(&ue->frame_parms, rxdataF[aa], slot_rx, 0, NR_NUMBER_OF_SYMBOLS_PER_SLOT, link_type_sl);
}
uint32_t ret = nr_ue_slsch_rx_procedures(ue, harq_pid, frame_rx, slot_rx, rxdataF, B_mul, 45727, proc);
if (ret != -1) {
bool polar_decoded = (ret < LDPC_MAX_LIMIT) ? true : false;
uint32_t ret = nr_ue_slsch_rx_procedures(ue, harq_pid, frame_rx, slot_rx, rxdataF, B_mul, Nidx, proc);
bool polar_decoded = (ret < LDPC_MAX_LIMIT) ? true : false;
uint16_t dest = (*harq->b_sci2 >> 32) & 0xFFFF;
bool dest_matched = (dest == node_id);
LOG_I(PHY, "dest %u vs %u node_id for hex %lx\n", dest, node_id, *harq->b_sci2);
if ((ret != -1) && dest_matched) {
if (payload_type_string)
validate_rx_payload_str(harq, slot_rx, polar_decoded);
else
validate_rx_payload(harq, frame_rx, slot_rx, polar_decoded);
}
if ((dest_matched == false) && (ue->sync_ref == 0) && (detect_new_dest == false)) {
if(ue->slss->sl_timeoffsetssb_r16 == 2) {
detect_new_dest = true;
ue->sync_ref = 1;
ue->slss->sl_timeoffsetssb_r16 = (node_id - 1) * 2 + 2;
ue->slss->sl_timeoffsetssb_r16_copy = ue->slss->sl_timeoffsetssb_r16;
init_mutex_of_relay_data();
}
}
if ((dest_matched == false) && ue->sync_ref) {
put_relay_data_to_buffer(harq->b, harq->b_sci2, harq->TBS);
}
}
}
LOG_D(PHY,"****** end Sidelink TX-Chain for AbsSlot %d.%d ******\n", frame_rx, slot_rx);
......@@ -1504,13 +1540,13 @@ validate_rx_payload(NR_DL_UE_HARQ_t *harq, int frame_rx, int slot_rx, bool polar
for (int i = 0; i < harq->TBS * 8; i++) {
estimated_output_bit[i] = (harq->b[i / 8] & (1 << (i & 7))) >> (i & 7);
test_input_bit[i] = (test_input[i / 8] & (1 << (i & 7))) >> (i & 7); // Further correct for multiple segments
if(i % 8 == 0){
if(i == 8 * 0) slot_tx = harq->b[0];
if(i == 8 * 1) frame_tx = harq->b[1];
if(i == 8 * 2) frame_tx += (harq->b[2] << 8);
if(i == 8 * 3) randm_tx = harq->b[3];
if(i >= 8 * comparison_beg_byte)
LOG_I(PHY,"TxByte : %4u vs %4u : RxByte\n", test_input[i / 8], harq->b[i / 8]);
if (i % 8 == 0) {
if (i == 8 * 0) slot_tx = harq->b[0];
if (i == 8 * 1) frame_tx = harq->b[1];
if (i == 8 * 2) frame_tx += (harq->b[2] << 8);
if (i == 8 * 3) randm_tx = harq->b[3];
if (i >= 8 * comparison_beg_byte)
LOG_I(PHY,"TxByte : %4u vs %4u : RxByte\n", test_input[i / 8], harq->b[i / 8]);
}
LOG_D(NR_PHY, "tx bit: %u, rx bit: %u\n", test_input_bit[i], estimated_output_bit[i]);
......
......@@ -110,11 +110,12 @@ int mu = 1;
int loglvl = OAILOG_WARNING;
int seed = 0;
int mcs = 0;
uint16_t node_id = 0;
static void get_sim_cl_opts(int argc, char **argv)
{
char c;
while ((c = getopt(argc, argv, "F:g:hIL:l:m:M:n:N:o:O:p:P:r:R:s:S:t:x:y:z:")) != -1) {
while ((c = getopt(argc, argv, "F:d:g:hIL:l:m:M:n:N:o:O:p:P:r:R:s:S:t:x:y:z:")) != -1) {
switch (c) {
case 'F':
input_fd = fopen(optarg, "r");
......@@ -124,6 +125,10 @@ static void get_sim_cl_opts(int argc, char **argv)
}
break;
case 'd':
node_id = atoi(optarg);
break;
case 'g':
switch((char)*optarg) {
case 'A':
......@@ -459,6 +464,7 @@ int main(int argc, char **argv)
r_re[i] = malloc16_clear(frame_length_complex_samples * sizeof(double));
r_im[i] = malloc16_clear(frame_length_complex_samples * sizeof(double));
}
get_softmodem_params()->node_number = node_id;
nr_ue_set_slsch_rx(rxUE, 0);
for (double SNR = snr0; SNR < snr1; SNR += snr_step) {
n_errors = 0;
......@@ -485,7 +491,7 @@ int main(int argc, char **argv)
char buffer1[rxUE->frame_parms.ofdm_symbol_size * 4];
for (int i = 0; i < 13; i++) {
bzero(buffer1, sizeof(buffer1));
printf("Slot %d, RXUE Symbol[%d]: %s\n",
LOG_D(PHY, "Slot %d, RXUE Symbol[%d]: %s\n",
slot, rxUE->frame_parms.ofdm_symbol_size * i,
hexdump((int16_t *)&rxUE->common_vars.rxdata[0][rxUE->frame_parms.ofdm_symbol_size * i],
rxUE->frame_parms.ofdm_symbol_size * 4, buffer1, sizeof(buffer1)));
......
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