# -*- coding: utf-8 -*-"""==========Scrambling==========Scrambling as a channel coding step masks transmitted bits by a pseudo-random sequenceknown at both receiver and transmitter in order to prevent long sequences of identicalbits.Therefore, most scrambling coding operations do not introduce redundancy to the scrambled bit blocks,i.e. the code rate is usually :math:`R = 1`."""from__future__importannotationsfromcollectionsimportdequefromtypingimportOptionalimportnumpyasnpfromhermespy.coreimportSerializablefrom.codingimportEncoder__author__="Jan Adler"__copyright__="Copyright 2024, Barkhausen Institut gGmbH"__credits__=["Jan Adler","Tobias Kronauer"]__license__="AGPLv3"__version__="1.3.0"__maintainer__="Jan Adler"__email__="jan.adler@barkhauseninstitut.org"__status__="Prototype"
[docs]classPseudoRandomGenerator:"""A generator for pseudo-random bit sequences. Generators with identical initialization will output identical random sequences. Implements pseudo-random generator as described in the 3GPP standard for :footcite:t:`2018:ts138211`. """__queue_x1:deque__queue_x2:deque__initial_queue_x1:deque__initial_queue_x2:dequedef__init__(self,init_sequence:np.ndarray,offset:int=1600)->None:""" Args: init_sequence(np.ndarray): A sequence of 31 bits initializing the generator. offset(int): Gold sequence parameter controlling the sequence offset. """# The required sequence buffer length is inferred from the length of the init sequencem=init_sequence.shape[0]# Make sure the buffer / init sequence is at least 4 bits lonifm<4:raiseValueError("The init sequence must contain at least 4 bits")# Init the first fifo queue as [1 0 0 ... 0]self.__queue_x1=deque(np.zeros(m,dtype=np.int8),m)self.__queue_x1.appendleft(1)# Init the second fifo queue by the provided init sequenceself.__queue_x2=deque(init_sequence,m)# Fast-forward the queues to compensate for the offsetfor_inrange(offset-m):self.__forward_x1()self.__forward_x2()# Store the initial queues in order to reset the rng to n = 0self.__initial_queue_x1=self.__queue_x1.copy()self.__initial_queue_x2=self.__queue_x2.copy()
[docs]defgenerate(self)->int:"""Generate the next bit within the pseudo-random sequence. Returns: int: The generated bit. """return(self.__forward_x1()+self.__forward_x2())%2
[docs]defgenerate_sequence(self,length:int)->np.ndarray:"""Generate a new sequence of random numbers. Args: length(int): Length of the sequence to be generated. Returns: A numpy array of dimension length containing a sequence of pseudo-random bits. """sequence=np.empty(length,dtype=int)forninrange(length):sequence[n]=self.generate()returnsequence
[docs]defreset(self)->None:"""Resets the gernator to its default state. This implies reverting the queues back to their original state (at rng position n = 0). """self.__queue_x1=self.__initial_queue_x1.copy()self.__queue_x2=self.__initial_queue_x2.copy()
[docs]classScrambler3GPP(Encoder,Serializable):"""Scrambler channel coding in the physical up- and down-link standard of the 3GPP. See section 7.3.1.1 of the respective technical standard :footcite:t:`2018:ts138211` for details. """yaml_tag:str="SCRAMBLER_3GPP"__transmit_rng:PseudoRandomGenerator__receive_rng:PseudoRandomGenerator__default_seed=np.array([0,1,0,1,1,0,1],int)def__init__(self,seed:Optional[np.ndarray]=None)->None:""" Args: seed (np.ndarray, optional): Seed used to initialize the scrambling sequence generation. Must contain a sequence of bits. """# Init base class (Encoder)Encoder.__init__(self)# Initialize the pseudo random number generatorseed=self.__default_seed.copy()ifseedisNoneelseseedself.__transmit_rng=PseudoRandomGenerator(seed)self.__receive_rng=PseudoRandomGenerator(seed)
[docs]classScrambler80211a(Encoder,Serializable):"""Scrambler channel coding in the the `802.11a` standard. Refer to section 17.3.5.4 of :footcite:t:`80211a:1999` for further details. """yaml_tag:str="SCRAMBLER_80211A"__seed:np.ndarray__queue:deque__default_seed:np.ndarray=np.array([0,1,0,1,1,0,1],dtype=int)def__init__(self,seed:Optional[np.ndarray]=None)->None:""" Args: seed (np.ndarray, optional): Seed used to initialize the scrambling sequence generation. Must contain a sequence of 7 bits. """# Init base class (Encoder)Encoder.__init__(self)# The default seed is all zerosself.__seed=self.__default_seed.copy()ifseedisNoneelseseedself.__queue=deque(self.__seed,7)@propertydefseed(self)->np.ndarray:"""Random sequence generator seed. Resets the internal register queue used to generate the scrambling sequence. Returns: np.ndarray: Numpy vector containing the generator seed. Must be an array of dimension 7 containing only soft bits. Raises: ValueError: If `seed` does not contain exactly 7 bits. """returnself.__seed@seed.setterdefseed(self,value:np.ndarray)->None:ifvalue.shape[0]!=7:raiseValueError("The seed must contain exactly 7 bit")forbitinvalue:ifbit!=0andbit!=1:raiseValueError("Only bits (i.e. 0 or 1) represent valid seed fields")self.__seed=valueself.__queue=deque(self.__seed,7)
def__forward_code_bit(self)->int:"""Generate the next bit in the scrambling sequence. The sequence depends on the configured seed. Returns: int: The next bit within the scrambling sequence """code_bit=(self.__queue[3]+self.__queue[6])%2self.__queue.appendleft(code_bit)returncode_bitdef__scramble_bit(self,bit:int)->int:"""Scramble a given bit. Args: bit(int): The bit to be scrambled. Returns: int: The scrambled bit."""return(self.__forward_code_bit()+bit)%2@propertydefbit_block_size(self)->int:return1@propertydefcode_block_size(self)->int:return1