6
6
"""
7
7
# pylint: disable=line-too-long
8
8
9
+ import functools
9
10
import numpy as np
10
- from .utils import generate_prng_seq
11
+ from .utils import generate_prng_seq , generate_low_papr_seq_type_1
11
12
from .config import Config
12
13
from sionna import nr
13
14
from .utils import calculate_tb_size
@@ -233,7 +234,7 @@ def n_rnti(self, value):
233
234
assert value in range (65536 ), "n_rnti must be in [0, 65535]"
234
235
self ._n_rnti = value
235
236
236
- #---transform_precoding ---#
237
+ #---precoding ---#
237
238
@property
238
239
def precoding (self ):
239
240
"""
@@ -427,9 +428,9 @@ def n(self):
427
428
used for DMRS generation
428
429
"""
429
430
if self .dmrs .config_type == 1 :
430
- n_max = self .num_resource_blocks * 12 // 4 - 1
431
+ n_max = self .num_effective_subcarriers // 4 - 1
431
432
elif self .dmrs .config_type == 2 :
432
- n_max = self .num_resource_blocks * 12 // 6 - 1
433
+ n_max = self .num_effective_subcarriers // 6 - 1
433
434
return list (range (n_max + 1 ))
434
435
435
436
@property
@@ -450,6 +451,31 @@ def num_resource_blocks(self):
450
451
else :
451
452
return self .n_size_bwp
452
453
454
+ @property
455
+ def num_effective_resource_blocks (self ):
456
+ """
457
+ int, read-only : Number of allocated resource blocks for the
458
+ PUSCH transmissions, that are actually used (can differ from
459
+ num_subcarriers when transform precoding is enabled,
460
+ because of constraints on the largest prime factor of the
461
+ subcarrier count)
462
+ """
463
+ @functools .lru_cache
464
+ def adjust_prbs_to_prime_factor_constraints (prbs ):
465
+ # Decreases the number of PRBs until the largest prime factor is at most 5
466
+ for eff_prbs in range (prbs , 1 , - 1 ):
467
+ n = eff_prbs
468
+ for p in [2 , 3 , 5 ]:
469
+ while n % p == 0 :
470
+ n /= p
471
+ if n == 1 :
472
+ return eff_prbs
473
+
474
+ if self .transform_precoding :
475
+ return adjust_prbs_to_prime_factor_constraints (self .num_resource_blocks )
476
+ else :
477
+ return self .num_resource_blocks
478
+
453
479
@property
454
480
def num_subcarriers (self ):
455
481
"""
@@ -458,6 +484,17 @@ def num_subcarriers(self):
458
484
"""
459
485
return 12 * self .num_resource_blocks
460
486
487
+ @property
488
+ def num_effective_subcarriers (self ):
489
+ """
490
+ int, read-only : Number of allocated subcarriers for the
491
+ PUSCH transmissions, that are actually used (can differ from
492
+ num_subcarriers when transform precoding is enabled,
493
+ because of constraints on the largest prime factor of the
494
+ subcarrier count)
495
+ """
496
+ return 12 * self .num_effective_resource_blocks
497
+
461
498
@property
462
499
def num_res_per_prb (self ):
463
500
"""
@@ -488,7 +525,7 @@ def dmrs_mask(self):
488
525
resource elements in the resource grid. `True` corresponds to
489
526
resource elements on which no data is transmitted.
490
527
"""
491
- mask = np .zeros ([self .num_subcarriers ,
528
+ mask = np .zeros ([self .num_effective_subcarriers ,
492
529
self .carrier .num_symbols_per_slot ],
493
530
dtype = bool )
494
531
@@ -503,7 +540,7 @@ def dmrs_mask(self):
503
540
cdm_ind [:,i ] = np .array ([0 ,1 , 6 , 7 ])+ 2 * i
504
541
505
542
for i in self .dmrs_symbol_indices :
506
- for j in range (self .num_resource_blocks ):
543
+ for j in range (self .num_effective_resource_blocks ):
507
544
for k in range (num_cdm_groups ):
508
545
mask [cdm_ind [:, k ] + 12 * j , i ] = True
509
546
return mask
@@ -518,7 +555,7 @@ def dmrs_grid(self):
518
555
This property returns for each configured DMRS port an empty
519
556
resource grid filled with DMRS signals as defined in
520
557
Section 6.4.1.1 [3GPP38211]. Not all possible options are implemented,
521
- e.g., frequency hopping and transform precoding are not available.
558
+ e.g., frequency hopping is not available.
522
559
523
560
This property provides the *unprecoded* DMRS for each configured DMRS port.
524
561
Precoding might be applied to map the DMRS to the antenna ports. However,
@@ -536,7 +573,7 @@ def dmrs_grid(self):
536
573
537
574
# Generate empty resource grid for each port
538
575
a_tilde = np .zeros ([len (self .dmrs .dmrs_port_set ),
539
- self .num_subcarriers ,
576
+ self .num_effective_subcarriers ,
540
577
self .carrier .num_symbols_per_slot ],
541
578
dtype = complex )
542
579
@@ -546,15 +583,23 @@ def dmrs_grid(self):
546
583
# For every l_prime
547
584
for l_prime in self .l_prime :
548
585
549
- # Compute c_init
550
586
l = l_bar + l_prime
551
- c_init = self .c_init (l )
552
587
553
- # Generate RNG
554
- c = generate_prng_seq (2 * self .num_subcarriers , c_init = c_init )
588
+ if self .transform_precoding :
589
+ if self .dmrs .n_sid is None :
590
+ n_id = self .carrier .n_cell_id
591
+ else :
592
+ n_id = self .dmrs .n_sid
593
+ r = generate_low_papr_seq_type_1 (self .num_effective_subcarriers // 2 , n_id % 30 , 0 , 0 )
594
+ else :
595
+ # Compute c_init
596
+ c_init = self .c_init (l )
597
+
598
+ # Generate RNG
599
+ c = generate_prng_seq (2 * self .num_effective_subcarriers , c_init = c_init )
555
600
556
- # Map to QAM
557
- r = 1 / np .sqrt (2 )* ((1 - 2 * c [::2 ]) + 1j * (1 - 2 * c [1 ::2 ]))
601
+ # Map to QAM
602
+ r = 1 / np .sqrt (2 )* ((1 - 2 * c [::2 ]) + 1j * (1 - 2 * c [1 ::2 ]))
558
603
559
604
# For every port in the dmrs port set
560
605
for j_ind , _ in enumerate (self .dmrs .dmrs_port_set ):
@@ -625,8 +670,38 @@ def precoding_matrix(self):
625
670
626
671
w /= np .sqrt (2 )
627
672
673
+ # Table 6.3.1.5-2
674
+ elif self .transform_precoding and self .num_antenna_ports == 4 :
675
+ w = np .zeros ([28 , 4 , 1 ], complex )
676
+
677
+ # TPMI index 0-7
678
+ w [:8 ,0 ,0 ] = [ 1 , 0 , 0 , 0 , 1 , 1 , 1 , 1 ]
679
+ w [:8 ,1 ,0 ] = [ 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 ]
680
+ w [:8 ,2 ,0 ] = [ 0 , 0 , 1 , 0 , 1 , - 1 , 1j ,- 1j ]
681
+ w [:8 ,3 ,0 ] = [ 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 ]
682
+
683
+ # TPMI index 8-15
684
+ w [8 :16 ,0 ,0 ] = [ 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 ]
685
+ w [8 :16 ,1 ,0 ] = [ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ]
686
+ w [8 :16 ,2 ,0 ] = [ 0 , 0 , 0 , 0 , 1 , 1j , - 1 ,- 1j ]
687
+ w [8 :16 ,3 ,0 ] = [ 1 , - 1 , 1j ,- 1j , - 1 , 1j , 1 ,- 1j ]
688
+
689
+ # TPMI index 16-23
690
+ w [16 :24 ,0 ,0 ] = [ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ]
691
+ w [16 :24 ,1 ,0 ] = [ 1j , 1j , 1j , 1j , - 1 , - 1 , - 1 , - 1 ]
692
+ w [16 :24 ,2 ,0 ] = [ 1 , 1j , - 1 ,- 1j , 1 , 1j , - 1 ,- 1j ]
693
+ w [16 :24 ,3 ,0 ] = [ 1j , 1 ,- 1j , - 1 , 1 ,- 1j , - 1 , 1j ]
694
+
695
+ # TPMI index 24-27
696
+ w [24 :28 ,0 ,0 ] = [ 1 , 1 , 1 , 1 ]
697
+ w [24 :28 ,1 ,0 ] = [- 1j ,- 1j ,- 1j ,- 1j ]
698
+ w [24 :28 ,2 ,0 ] = [ 1 , 1j , - 1 ,- 1j ]
699
+ w [24 :28 ,3 ,0 ] = [- 1j , - 1 , 1j , 1 ]
700
+
701
+ w /= 2
702
+
628
703
# Table 6.3.1.5-3
629
- elif self .num_antenna_ports == 4 :
704
+ elif not self . transform_precoding and self .num_antenna_ports == 4 :
630
705
w = np .zeros ([28 ,4 ,1 ], complex )
631
706
632
707
# TPMI index 0-7
@@ -825,7 +900,7 @@ def num_coded_bits(self):
825
900
n_re_per_prb = self .num_res_per_prb - self .num_ov
826
901
827
902
# number of allocated REs
828
- n_re = n_re_per_prb * self .num_resource_blocks
903
+ n_re = n_re_per_prb * self .num_effective_resource_blocks
829
904
830
905
# total number of bits per slot
831
906
num_coded_bits = int (self .tb .tb_scaling * self .tb .num_bits_per_symbol \
@@ -842,7 +917,7 @@ def tb_size(self):
842
917
843
918
# number of allocated REs
844
919
# the max. number of REs per PRB is limited to 156 in 38.214
845
- n_re = min (156 , n_re_per_prb ) * self .num_resource_blocks
920
+ n_re = min (156 , n_re_per_prb ) * self .num_effective_resource_blocks
846
921
847
922
# include tb_scaling as defined in Tab. 5.1.3.2-2 38.214
848
923
target_tb_size = int (self .tb .target_coderate * self .tb .tb_scaling \
@@ -924,6 +999,14 @@ def check_config(self):
924
999
assert self .num_layers == self .num_antenna_ports ,\
925
1000
"num_layers must be == num_antenna_ports"
926
1001
1002
+ if self .transform_precoding :
1003
+ assert self .num_layers == 1 ,\
1004
+ "When transform precoding is used, only a single MIMO layer is supported"
1005
+ assert self .dmrs .config_type == 1 , \
1006
+ "When transform precoding is used, DMRS config type must be 1"
1007
+ assert self .dmrs .num_cdm_groups_without_data == 2 , \
1008
+ "When transform precoding is used, num_cdm_groups_without_data must be 2"
1009
+
927
1010
# Check Tables 6.4.1.1.3-3/4 are valid
928
1011
if self .dmrs .length == 1 :
929
1012
if self .mapping_type == "A" :
@@ -1033,11 +1116,13 @@ def check_pusch_configs(pusch_configs):
1033
1116
"num_tx" : len (pusch_configs ),
1034
1117
"num_layers" : pc .num_layers ,
1035
1118
"num_subcarriers" : pc .num_subcarriers ,
1119
+ "num_effective_subcarriers" : pc .num_effective_subcarriers ,
1036
1120
"num_ofdm_symbols" : pc .symbol_allocation [1 ],
1037
1121
"subcarrier_spacing" : pc .carrier .subcarrier_spacing * 1e3 ,
1038
1122
"num_antenna_ports" : pc .num_antenna_ports ,
1039
1123
"precoding" : pc .precoding ,
1040
1124
"precoding_matrices" : [],
1125
+ "transform_precoding" : pc .transform_precoding ,
1041
1126
"pusch_config" : pc ,
1042
1127
"carrier_config" : pc .carrier ,
1043
1128
"num_coded_bits" : pc .num_coded_bits ,
0 commit comments