[Feature]Upload Modem source code

Change-Id: Id4294f30faced84d3e6fd6d5e61e1111bf287a37
diff --git a/mcu/tools/IQ_Analyzer/README.txt b/mcu/tools/IQ_Analyzer/README.txt
new file mode 100644
index 0000000..2b6cb8f
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/README.txt
@@ -0,0 +1,31 @@
+===================================================

+IQ Analyzer Tool source codes

+===================================================

+====================================================

+Creating setup

+====================================================

+

+1. Install py2exe (64bit for python 2.7)

+

+         install from:

+   https://sourceforge.net/projects/py2exe/files/py2exe/0.6.9/py2exe-0.6.9.win64-py2.7.amd64.exe/download

+

+2. Update matplotlib

+

+         type to python console:

+         pip install -U matplotlib

+

+3. Create empty file named __init__.py to C:\Users\<MTK_USER_ID>\AppData\Local\Continuum\anaconda2\Lib\site-packages\mpl_toolkits

+

+4. Run setup script

+

+        Go to IQ tool directory and type to python console:

+        python setup.py py2exe 

+

+

+====================================================

+Creating setup using pyinstaller

+====================================================

+1. create folder: c:\Users\builder\mc3\conda-bld\qt_1505945264697\_h_env\Library\plugins

+2. add pyqt5.dll and pyqt5qmlplgin.dll to the folder

+3. pyinstaller --onefile <python_file.py>
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/DCKA_polar_decoder.py b/mcu/tools/IQ_Analyzer/src/DCKA_polar_decoder.py
new file mode 100644
index 0000000..9b8e9c5
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/DCKA_polar_decoder.py
@@ -0,0 +1,397 @@
+from get_crc_generator_matrix import get_crc_generator_matrix

+import IQ_Utils

+from IQ_Utils import L

+

+import numpy as np

+

+###############################################################

+# Log-based or min-sum update rule

+# NOTE: Implements equation (9) from Balatsoukas-Stimming...

+# http://dx.doi.org/10.1109/TSP.2015.2439211

+# input: recursives

+# output: updated llr

+###############################################################  

+def minstar(a, b):

+        

+    global approx_minstar

+    

+    if approx_minstar or abs(a) == np.inf or abs(b) == np.inf:

+        c = np.dot(np.dot(np.sign(a), np.sign(b)), min(abs(a), abs(b)))

+    else:

+        c = np.dot(2, np.arctanh(np.dot(np.tanh(a / 2), np.tanh(b / 2))))

+     

+    return c

+

+##########################################################################

+# Function to compute path-metrics 

+# NOTE: Implements equations (11b) and (12) from Balatsoukas-Stimming...

+# http://dx.doi.org/10.1109/TSP.2015.2439211

+# input: Previous path metric, log-likehood ratio, bit

+# output: Updated path metric

+##########################################################################  

+def phi(PM_iminus1, L_i, u_i):

+

+    global approx_minstar

+    

+    if approx_minstar:

+        PM_i = PM_iminus1

+        flags = np.dot(0.5, (1 - np.sign(L_i))) != u_i

+        PM_i[flags] = PM_i[flags] + abs(L_i[flags])

+    else:

+        PM_i = np.add(np.transpose(PM_iminus1), np.log(1 + np.exp(-(1-2*u_i)*L_i)))

+    return np.transpose(PM_i)

+

+######################################################################

+# Broadcast and update various bits after the knowledge of the latest 

+# bit decision at index i

+# NOTE: global

+# input: bit at index i

+######################################################################  

+def update_bit(row,col):

+        

+    global bits

+    global bits_updated

+        

+    offset = bits.shape[1] / (2 ** (bits.shape[2] - col))

+

+    for l in range(0, bits.shape[0]):

+        if np.mod(row, np.dot(2, offset)) >= offset:

+            if np.logical_not(bits_updated[row,col-1]):

+                update_bit(row, col-1)

+            bits[l,row,col] = bits[l,row,col-1]

+        else:

+            if np.logical_not(bits_updated[row,col-1]):

+                update_bit(row, col-1)

+            if np.logical_not(bits_updated[row+offset,col-1]):

+                update_bit(row+offset, col-1)

+            bits[l,row,col] = np.fmod(bits[l,row,col-1] + bits[l,row+offset,col-1], 2)

+    

+    bits_updated[row,col] = True

+    

+######################################################################

+# Compute the i-th bit-likelihood by using a computational-tree of 

+# different likelihood transformation operation applied for N received 

+# likelihoods

+# NOTE: global

+# input: log-likelihood at index i

+######################################################################      

+def update_llr(row, col):

+    

+    global llrs

+    global llrs_updated

+    global bits

+    global bits_updated

+    

+    offset=bits.shape[1] / (2 ** (bits.shape[2] - col - 1))

+    

+    for l in range(0, bits.shape[0]):

+        if np.mod(row, np.dot(2, offset)) >= offset:

+            if np.logical_not(bits_updated[row-offset,col]):

+                update_bit(row-offset, col)

+            if np.logical_not(llrs_updated[row-offset,col+1]):

+                update_llr(row-offset, col+1)

+            if np.logical_not(llrs_updated[row,col+1]):

+                update_llr(row, col+1)

+            # 8b equation

+            llrs[l,row,col] = np.dot((- 1) ** bits[l,row-offset,col], llrs[l,row-offset,col+1]) + llrs[l,row,col+1]

+

+        else:

+            if np.logical_not(llrs_updated[row,col+1]):

+                update_llr(row, col+1)

+            if np.logical_not(llrs_updated[row+offset,col+1]):

+                update_llr(row+offset, col+1)

+            # 8a equation

+            llrs[l,row,col] = minstar(llrs[l,row,col+1], llrs[l,row+offset,col+1])

+    

+    llrs_updated[row,col] = True

+  

+#####################################################

+# Get last non-zero index in crc generator matrix

+# NOTE: N/A

+# input: crc polynomial

+# output: last non-zero index in crc polynomial

+##################################################### 

+def get_last_non_zero_index(d, default=None): 

+    rev = (len(d) - idx for idx, item in enumerate(reversed(d), 1) if item)  

+    return next(rev, default)

+

+#################################################################################################################################

+# DCKA_POLAR_DECODER Distributed-CRC-and-Known-bit-Aided (DCKA) polar decoder decodes the encoded LLR sequence e_tilde, 

+#in order to obtain the recovered information bit sequence a_hat.

+# NOTE: N/A

+# input: 

+#        e_tilde should be a real row vector comprising E number of Logarithmic Likelihood Ratios (LLRS), 

+#        each having a value obtained as LLR = ln(P(bit=0)/P(bit=1)).

+#        crc_polynomial_pattern should be a binary row vector comprising P+1 number of bits, each having the value 0 or 1. 

+#        These bits parameterise a Cyclic Redundancy Check (CRC) comprising P bits. Each bit provides the coefficient of the 

+#        corresponding element in the CRC generator polynomial. From left to right, the bits provide the coefficients for the

+#        elements D^P, D^P-1, D^P-2, ..., D^2, D, 1.   

+#        

+#        crc_interleaver_pattern should be a row vector comprising K number of integers, each having a unique value in the 

+#        range 1 to K. Each integer identifies which one of the K information or CRC bits provides the corresponding bit in the 

+#        input to the polar encoder kernal.

+#        

+#        info_bit_pattern should be a row vector comprising N number of logical elements, each having the value true or false. 

+#        The number of elements in info_bit_pattern having the value true should be K, where K = A+P. These elements having the 

+#        value true identify the positions of the information and CRC bits within the input to the polar encoder kernal. 

+#        

+#        rate_matching_pattern should be a row vector comprising E number of integers, each having a value in the range 1 to N. 

+#        Each integer identifies which one of the N outputs from the polar encoder kernal provides the corresponding bit in the 

+#        encoded bit sequence e.              

+#        

+#        mode should have the value 'repetition', 'puncturing' or 'shortening'. This specifies how the rate matching has been

+#        achieved. 'repetition' indicates that some outputs of the polar encoder kernal are repeated in the encoded bit sequence 

+#        two or more times. 'puncturing' and 'shortening' indicate that some outputs of the polar encoder kernal have been excluded 

+#        from the encoded bit sequence. In the case of 'puncturing' these excluded bits could have values of 0 or 1. In the case of 

+#        'shortening' these excluded bits are guaranteed to have values of 0.       

+#        

+#        list_size should be a scalar integer. It specifies the list size to use during Successive Cancellation List (SCL) decoding.

+#        

+#        min_sum shoular be a scalar logical. If it is true, then the SCL decoding process will be completed using the min-sum 

+#        approximation. Otherwise, the log-sum-product will be used. The log-sum-product gives better error correction capability 

+#        than the min-sum, but it has higher complexity.

+#        

+#        P2 should be a scalar integer. Although the CRC has P bits, only P-min(P2,log2(L)) of these are used for error detection. 

+#        The remaining min(P2,log2(L)) of the CRC bits are used to improve error correction. So the CRC needs to be min(P2,log2(L)) 

+#        number of bits longer than CRCs used in other codes, in order to achieve the same error detection capability. 

+

+# output: 

+#         a_hat (32 bit)

+#################################################################################################################################  

+def DCKA_polar_decoder(e_tilde, crc_polynomial_pattern, crc_interleaver_pattern, info_bit_pattern, rate_matching_pattern, mode, list_size, min_sum, P2):

+

+    E = len(e_tilde)

+    N = len(info_bit_pattern)

+    K = len(crc_interleaver_pattern)

+    P = len(crc_polynomial_pattern) - 1

+    A = K - P

+    

+    if np.log2(N) != round(np.log2(N)):

+        raise Exception('N should be a power of 2')

+    

+    if sum(info_bit_pattern) != K:

+        raise Exception('info_bit_pattern should contain K number of ones.')

+    

+    if max(rate_matching_pattern) > N:

+        raise Exception('rate_matching_pattern is not compatible with N')

+        

+    

+    if 'repetition' in mode:

+        if E < N:

+            raise Exception('mode is not compatible with E')

+    else:

+        if 'puncturing' in mode:

+            if E >= N:

+                raise Exception('mode is not compatible with E')

+        else:

+            if 'shortening' in mode:

+                if E >= N:

+                    raise Exception('mode is not compatible with E')

+            else:

+                raise Exception('Unsupported mode')

+        

+    # This global variable is used by the minstar and phi functions.

+    global approx_minstar

+    approx_minstar = min_sum

+

+    ## Characterise the interleaved CRC

+    

+    # Get the CRC generator matrix, which has dimensions A by P.

+    G_P = get_crc_generator_matrix(A, crc_polynomial_pattern)

+    

+    # Extend the CRC generator matrix by append an identity matrix to 

+    # represent the CRC bits, giving dimenstions K by P.    

+    G_P2 = np.append(G_P, np.eye(P, dtype=int), 0)

+    

+    # Interleave the rows of the extended CRC generator matrix, according to

+    # the CRC interleaver.

+    G_P3 = G_P2[crc_interleaver_pattern,:]

+

+    # Determine where the last 1-valued bit appears in each column. When the 

+    # SCL decoding process reaches the corresponding interleaved CRC bit, we 

+    # will terminate the decoding process if all list entries have failed the 

+    # checks assocated with at least one of this or the preceeding CRC bits.

+    last_one_index = [0]*P

+    for p in xrange(0, P):

+        last_one_index[p] = get_last_non_zero_index(G_P3[:,p])

+    

+    ## Rate matching

+    if 'repetition' in mode:

+        # LLRs for repeated bits are added together.

+        d_tilde=np.zeros((N,1))

+        for i in xrange(0, E):

+            d_tilde[rate_matching_pattern[i],0] = d_tilde[rate_matching_pattern[i],0] + e_tilde[i]

+    else:

+        if 'puncturing' in mode:

+            # Zero valued LLRs are used for punctured bits, because the decoder

+            # doesn't know if they have values of 0 or 1.

+            d_tilde = np.zeros((N,1))

+        else:

+            if 'shortening' in mode:

+                # Infinite valued LLRs are used for shortened bits, because the 

+                # decoder knows that they have values of 0.

+                d_tilde = np.inf((1,N))

+            else:

+                raise Exception('Unknown rate matching mode')

+        d_tilde[rate_matching_pattern] = e_tilde

+        

+    ## Perform the SCL polar decoder kernal operation.

+    # This is achieved according to the algorithm described in 

+    # http://ieeexplore.ieee.org/abstract/document/7114328/

+

+    global bits

+    global bits_updated

+    global llrs

+    global llrs_updated

+        

+    bits = np.zeros((1,N,int(np.log2(N))+1), dtype=int) # Initialise all bits to zero. The left-most column corresponds to the decoded information, CRC and frozen bits

+

+    info_bit_pattern_log_not = np.zeros((len(info_bit_pattern),1), dtype=bool)

+    info_bit_pattern_log_not[:,0] = np.logical_not(info_bit_pattern)    # logical not info bit pattern

+    

+    # The zero values that have initialised the frozen bits are known to be correct

+    bits_updated = np.zeros((N,int(np.log2(N))+1))

+    bits_updated[:][:]=np.hstack((info_bit_pattern_log_not, np.zeros((N,int(np.log2(N))), dtype=bool)))

+    

+    # d_tilde transpose

+    d_tilde_transposed = np.zeros((len(d_tilde),1), dtype=float)

+    d_tilde_transposed[:,0]=d_tilde[:,0]

+    

+    # Initialise the LLRs. The right-most column corresponds to the received LLRS

+    llrs = np.zeros((1,N,int(np.log2(N))+1))

+    llrs[:][:][:] = np.hstack((np.zeros((N, int(np.log2(N)))), d_tilde_transposed))    

+

+    # The received LLRs have been updated.

+    llrs_updated = np.zeros((N, int(np.log2(N))+1))

+    llrs_updated[:][:] = np.hstack((np.zeros((N, int(np.log2(N))), dtype=bool),np.ones((N,1), dtype=bool))) 

+

+    PM = np.zeros((1,1,1), dtype=float) # Initialise the path metrics

+    

+    L_prime = 1 # Initialise the list size to 1. This will grow as the decoding proceeds

+    

+    # We will calculate the CRC checksums alongside the SCL decoding process.

+    crc_checksums = np.zeros((1, P, 1), dtype=bool)

+

+    # We will keep track of whether any of the checks associated with the CRC

+    # bits have failed.

+    crc_okay = np.ones((1,1,1),dtype=bool)

+    

+    # We need a counter to keep track of which information or CRC bit we are

+    # working on.

+    i2 = 0

+    

+    # We will return an empty vector if all list entries have failed the checks 

+    # assocated with at least one of the CRC bits.

+    a_hat = []

+   

+    # Consider each bit in turn

+    for i in xrange(0, N):

+        # Make recursive function calls to perform the XOR, g and f functions

+        # necessary to obtain the corresponding LLR

+        update_llr(i, 0)    

+        if info_bit_pattern[i] == 0:

+            PM = phi(PM, llrs[:,i,0], 0)

+

+        else: # Information or CRC bit   

+            # Double the list size, using 0-valued bits for the first half and 1-valued bits for the other half

+            PM = np.concatenate((phi(PM, llrs[:,i,0], 0), phi(PM, llrs[:,i,0], 1)), axis=0)

+            

+            llrs = np.concatenate((llrs, llrs), axis=0)

+            bits = np.concatenate((bits, bits), axis=0)                   

+            bits[0:L_prime,i,0] = 0

+            bits[L_prime:2*L_prime,i,0] = 1

+            bits_updated[i,0] = True

+

+            # We use the interleaved CRC generator matrix to update the CRC 

+            # check sums whenever an information or CRC bit adopts a value of

+            # 1.

+            G_P3_repmat = np.transpose(np.tile(G_P3[i2,:], [L_prime, 1,1]), (0, 2, 1)) # repeat crc generator matrix 

+            crc_checksums = np.concatenate((crc_checksums, np.mod((crc_checksums+G_P3_repmat), 2)), axis=0)

+            

+            # We need to keep track of whether any of the checks associated 

+            # with the previous CRC bits have failed.

+            crc_okay = np.concatenate((crc_okay, crc_okay), axis=0)                  

+            

+            # If the list size has grown above list_size, then we need to find and keep only the best list_size entries in the list

+            L_prime = bits.shape[0]

+            if L_prime > list_size:

+                max_indices = np.argsort(np.sort(PM), axis=0)

+                max_indices = np.reshape(max_indices, L_prime)

+

+                PM = PM[max_indices[0:list_size],:,:]

+                bits = bits[max_indices[0:list_size],:,:]

+                llrs = llrs[max_indices[0:list_size],:,:]

+                crc_checksums = crc_checksums[max_indices[0:list_size],:,:]

+                crc_okay = crc_okay[max_indices[0:list_size],:,:]

+                L_prime = list_size  

+

+        # We check the corresponding CRC checksums whenever we reach the

+        # last 1-valued bit in a column of the interleaved CRC generator

+        # matrix.

+            check_crc_bits = [j for j,x in enumerate(last_one_index) if x==i2] 

+            for crc_bit_index in xrange(0, len(check_crc_bits)):

+                for list_index in xrange(0, L_prime):

+                    # The checksum should equal 0. If it equals 1, then the CRC

+                    # check has failed. Note that we should not prune these

+                    # entries from the list, even though we know that they will

+                    # fail the CRC. We should continue the decoding of this

+                    # list entries, otherwise we will damage the error

+                    # detection capability of the CRC.

+                   

+                    if crc_checksums[list_index,check_crc_bits[crc_bit_index],0] == 1:

+                        # We keep track of the failing check.

+                        crc_okay[list_index,0,0] = False

+

+            # We terminate the decoding process if all list entries have failed 

+            # the checks assocated with at least one of this or the preceeding 

+            # CRC bits.

+            if np.logical_not(any(crc_okay)):

+                print "Decoding process terminated"

+                return 0           

+                

+            # Increment the counter of information and CRC bits

+            i2=i2 + 1

+    

+    ## Information bit extraction

+    

+    # We use the list entry with a passing CRC that has the best metric. But we

+    # only consider the best min(L,2^P2) entries in the list, to avoid

+    # degrading the CRC's error detection capability.

+    max_indices = np.argsort(np.sort(PM,axis=0), axis=0)

+    max_indices = np.reshape(max_indices, max_indices.shape[0])

+

+    b_hat = []

+

+    for list_index in xrange(0, min([L,2 ** P2])):

+        # Consider the next best list entry.

+        # We already checked the CRC during the SCL decoding process.

+        if crc_okay[max_indices[list_index]]:

+            u_hat = np.transpose(bits[max_indices[list_index],:,0])

+

+            # Extract the information bits from the output of the polar decoder 

+            # kernal.            

+            c_hat = []

+            for position, item in enumerate(info_bit_pattern):

+                if item == 1:

+                    c_hat.append(u_hat[position])

+            

+            item = 0

+            for i in xrange(len(crc_interleaver_pattern)):

+                np_crc_interleaver_pattern = np.array(crc_interleaver_pattern)  # crc interleaver pattern to numpy array format

+                item_index_tmp = np.where(np_crc_interleaver_pattern == item)   # find index for interleaving

+                item = item + 1

+                

+                # dummy manipulation 

+                item_index_tmp2 = item_index_tmp[0]

+                item_index = item_index_tmp2[0]

+

+                b_hat.append(c_hat[item_index])  # Deinterleave the information and CRC bits.               

+             

+            # Remove the CRC and output the information bits.

+            a_hat = b_hat[0:-P]

+

+            break     

+   

+    print "   Transport block before scrambling and interleaving (32 bit) : ", a_hat

+    return a_hat       

+        
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/IQ_Decoder.py b/mcu/tools/IQ_Analyzer/src/IQ_Decoder.py
new file mode 100644
index 0000000..d2d9a56
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/IQ_Decoder.py
@@ -0,0 +1,188 @@
+import numpy as np

+from IQ_Utils import L

+

+class SS_Decoder:

+    def __init__(self, params, signals):

+        # 10 Last samples used only on drawing.

+        self.PSS_corr_magSQR_output_frame_main = [0] * 10 * params.analysis_frame_len

+        self.SSS_corr_magSQR_output_frame_main = [0] * 10 * params.analysis_frame_len

+        # We have found PSS singal

+        self.PSS_peak_detected = 0

+        # We have found SSS signal

+        self.SSS_peak_detected = 0

+        # Index in our buffer, previous index

+        self.hold_PSS_max_idx = 0

+        self.hold_SSS_max_idx = 0

+        # Do Inverse Fast Fourier Transformation        

+        self.doIFFT = 0

+        # Index on our index current.

+        self.PSS_Subframe_max_idx = 0

+        self.SSS_Subframe_max_idx = 0

+        self.params = params

+        self.signals = signals

+        self.PSS_Subframe_max_val = 0

+        self.SSS_Subframe_max_val = 0

+        # 3 Samples.

+        self.IQ_ThreeframeBuffer_main = [0] * 3 * params.analysis_frame_len        

+        

+    def pss_run(self, IQ_frame_main_norm):

+        params = self.params

+

+        # Keep feeding our 3 sample buffer, newest data to last 

+        self.IQ_ThreeframeBuffer_main[:2*params.analysis_frame_len] = self.IQ_ThreeframeBuffer_main[params.analysis_frame_len:]

+        self.IQ_ThreeframeBuffer_main[2*params.analysis_frame_len:] = IQ_frame_main_norm

+        #if (params.is_NR==1):

+        if (0):

+            # Reset 4 symbols of extension

+            self.IQ_ThreeframeBuffer_main[3*params.analysis_frame_len-params.analysis_frame_overlap:] = [0] * params.analysis_frame_overlap

+        

+        PSS_TD_seq = self.signals.PSS_TD_complex_pad_fftSize_wShift_ifft # Local

+        

+        # Cross-correlation with our reference signal to find reference singal        

+        IQ_ThreeframeBuffer_PSScorr_main = np.correlate(self.IQ_ThreeframeBuffer_main,PSS_TD_seq,'full')

+        #print [len(IQ_ThreeframeBuffer_PSScorr_main), len(self.IQ_ThreeframeBuffer_main), len(PSS_TD_seq)]

+        

+        # Get middle sample.    

+        IQ_frame_PSScorr_main = IQ_ThreeframeBuffer_PSScorr_main[params.analysis_frame_len:2*params.analysis_frame_len]

+        

+        # Convert our sample to abs.        

+        IQ_frame_ABS_PSScorr_main = np.absolute(IQ_frame_PSScorr_main)

+            

+        # Keep feeding our 10 sample buffer (10ms one radioframe). Add current sample to last.

+        self.PSS_corr_magSQR_output_frame_main[:9*params.analysis_frame_len] = self.PSS_corr_magSQR_output_frame_main[params.analysis_frame_len:]

+        self.PSS_corr_magSQR_output_frame_main[9*params.analysis_frame_len:]    = IQ_frame_ABS_PSScorr_main

+            

+        self.PSS_Subframe_max_idx = np.argmax(IQ_frame_ABS_PSScorr_main) # Find maximun value from our data "Maximum Likehood"

+        self.PSS_Subframe_max_val = IQ_frame_ABS_PSScorr_main[self.PSS_Subframe_max_idx]

+            

+        # Calculate average level

+        PSS_Subframe_mean = np.mean(IQ_frame_ABS_PSScorr_main)

+    

+        # Find peak

+        if (params.is_TDD == 1):

+            PSS_threshold = 0.2

+        else:

+            PSS_threshold = 0.1

+

+        if ((self.PSS_Subframe_max_val > 2.0*PSS_Subframe_mean)and(self.PSS_Subframe_max_val > PSS_threshold)):

+            self.doIFFT = 1

+            self.PSS_peak_detected = 1

+                

+            if (self.hold_PSS_max_idx == 0):

+                self.hold_PSS_max_idx = self.PSS_Subframe_max_idx

+                

+                

+            L.info( 'PSS peak idx: ' + str(self.PSS_Subframe_max_idx) )

+            L.info( 'PSS max: ' +str(self.PSS_Subframe_max_val) )

+            

+        else:

+            self.doIFFT = 0

+            self.PSS_peak_detected = 0

+

+    def nr_sss_run(self, fetch_SSS_seq):

+        params = self.params

+

+        SSS_TD_seq = self.signals.SSS_TD_complex_pad_fftSize_wShift_ifft # Local

+

+        # Cross-correlation with our reference signal to find reference singal        

+        IQ_ThreeframeBuffer_SSScorr_main = np.correlate(self.IQ_ThreeframeBuffer_main,SSS_TD_seq,'full')

+

+        # Get middle sample.    

+        IQ_frame_SSScorr_main = IQ_ThreeframeBuffer_SSScorr_main[params.analysis_frame_len:2*params.analysis_frame_len]

+

+        # Convert our sample to abs.        

+        IQ_frame_ABS_SSScorr_main = np.absolute(IQ_frame_SSScorr_main)

+

+        # Keep feeding our 10 sample buffer (10ms one radioframe). Add current sample to last.

+        self.SSS_corr_magSQR_output_frame_main[:9*params.analysis_frame_len] = self.SSS_corr_magSQR_output_frame_main[params.analysis_frame_len:]

+        self.SSS_corr_magSQR_output_frame_main[9*params.analysis_frame_len:]    = IQ_frame_ABS_SSScorr_main

+

+        self.SSS_Subframe_max_idx = np.argmax(IQ_frame_ABS_SSScorr_main) # Find maximun value from our data "Maximum Likehood"

+        self.SSS_Subframe_max_val = IQ_frame_ABS_SSScorr_main[self.SSS_Subframe_max_idx]

+

+        # Calculate average level

+        SSS_Subframe_mean = np.mean(IQ_frame_ABS_SSScorr_main)

+    

+        # Find peak

+        if (params.is_TDD == 1):

+            SSS_threshold = 0.2

+        else:

+            SSS_threshold = 0.1

+

+        if ((self.SSS_Subframe_max_val > 2.0*SSS_Subframe_mean)and(self.SSS_Subframe_max_val > SSS_threshold)):

+            self.SSS_peak_detected = 1

+            L.info( 'SSS peak idx: ' + str(self.SSS_Subframe_max_idx) )

+            L.info( 'SSS max: ' +str(self.SSS_Subframe_max_val) )

+        else:

+            self.SSS_peak_detected = 0

+        #Correlation Subframe

+        SSS_corr = np.sum(np.multiply(fetch_SSS_seq,self.signals.d_sss))

+        SSS_corr_abs_sqr = np.square(np.absolute(SSS_corr))

+        return 0

+

+    def lte_sss_run(self, fetch_SSS_seq):

+        # Deinterleave SSS

+        SSS_seq_2k = fetch_SSS_seq[::2]

+        SSS_seq_2kPlus1 = fetch_SSS_seq[1::2]

+

+        # ***** 2k processing part *****

+

+        # Pre-generate seq for correlation with SSS (2k)

+        s0c0_ = np.multiply(self.signals.s0_,self.signals.c0_)    #For Subfame_0

+        s1c0_ = np.multiply(self.signals.s1_,self.signals.c0_)  #For Subfame_5

+

+        # Correlation Subframe_0

+        SSS_corr_s0c0 = np.sum(np.multiply(SSS_seq_2k,s0c0_))

+        SSS_corr_abs_sqr_s0c0 = np.square(np.absolute(SSS_corr_s0c0))

+

+        # Correlation Subframe_5

+        SSS_corr_s1c0 = np.sum(np.multiply(SSS_seq_2k,s1c0_))

+        SSS_corr_abs_sqr_s1c0 = np.square(np.absolute(SSS_corr_s1c0))

+

+        #print [SSS_corr_abs_sqr_s0c0, SSS_corr_abs_sqr_s1c0]

+        #print ['SSS subframe',np.argmax([SSS_corr_abs_sqr_s0c0, SSS_corr_abs_sqr_s1c0])]

+

+        # ***** 2k+1 processing part *****

+

+        # Pre-generate seq for correlation with SSS (2k)

+        #For Subfame_0

+        s1c1_ = np.multiply(self.signals.s1_,self.signals.c1_)                

+        s1c1z1m0 = np.multiply(s1c1_,self.signals.z1_m0)

+        #For Subfame_5

+        s0c1_ = np.multiply(self.signals.s0_,self.signals.c1_)

+        s0c1z1m1 = np.multiply(s0c1_,self.signals.z1_m1)

+

+        # Correlation Subframe_0

+        SSS_corr_s1c1z1m0 = np.sum(np.multiply(SSS_seq_2kPlus1,s1c1z1m0))

+        SSS_corr_abs_sqr_s1c1z1m0 = np.square(np.absolute(SSS_corr_s1c1z1m0))

+

+        # Correlation Subframe_5

+        SSS_corr_s0c1z1m1 = np.sum(np.multiply(SSS_seq_2kPlus1,s0c1z1m1))

+        SSS_corr_abs_sqr_s0c1z1m1 = np.square(np.absolute(SSS_corr_s0c1z1m1))

+

+        #print [SSS_corr_abs_sqr_s1c1z1m0, SSS_corr_abs_sqr_s0c1z1m1]

+

+        # Frame Timing Decision module

+        SSS_metric_Subframe0 = SSS_corr_abs_sqr_s0c0 + SSS_corr_abs_sqr_s1c1z1m0  

+        SSS_metric_Subframe5 = SSS_corr_abs_sqr_s1c0 + SSS_corr_abs_sqr_s0c1z1m1

+        #print[SSS_metric_Subframe0,SSS_metric_Subframe5]

+

+        #ToDo: Revise threshold condition below

+        if (SSS_metric_Subframe0 > 10.0*SSS_metric_Subframe5):

+            start_half_frame = 0

+        #ToDo: Revise threshold condition below

+        if (SSS_metric_Subframe5 > 10.0*SSS_metric_Subframe0):

+            start_half_frame = 1 

+

+#        else:

+#            start_half_frame = 1

+        #print "half_frame = " + str(start_half_frame)

+        half_subframe_offset = start_half_frame * 70

+

+        # SSS HACK!!! (Needed for LowRX)

+        if (start_half_frame == 1):

+            start_half_frame = 0

+        else:

+            start_half_frame = 1

+        self.SSS_peak_detected = 0

+        return half_subframe_offset
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/IQ_Params.py b/mcu/tools/IQ_Analyzer/src/IQ_Params.py
new file mode 100644
index 0000000..6a11623
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/IQ_Params.py
@@ -0,0 +1,137 @@
+

+###################################################################################################

+#                                                                    L T E   D E F I N I T I O N S                                                                    #

+###################################################################################################

+###################################################################################################

+#        LTE Bandwidth | 1.4 MHz   | 3.0 MHz   | 5.0 MHz   | 10.0 MHz   | 15.0 MHz   | 20.0 MHz   #

+#          Num. of RBs |     6     |    15     |    25     |     50     |            |    100     #

+# Occupied Subcarriers |    72     |   180     |   300     |    600     |            |   1200     #

+#                Nfft  |   128     |   256     |   512     |   1024     |            |   2048     #

+#                N_CP1 |    10     |    20     |    40     |     80     |            |    160     #

+#                N_CP2 |     9     |    18     |    36     |     72     |            |    144     #

+#      LTE_sample_rate | 1.92 Msps | 3.84 Msps | 7.68 Msps | 15.36 Msps | 23.04 Msps | 30.72 Msps #

+###################################################################################################

+# Notes:                                                                                          #

+# LTE_sample_rate  = 2*[(N_DL_sym-1)(N_fft+N_CP2) + (N_fft+N_CP1)] / (1 millisecond)              #

+#                                                                                                 #

+###################################################################################################

+

+####################################################################

+# ------------------ LTE-SPECIFIC PARAMATERS (B) ------------------#

+####################################################################

+

+

+class IqParams:

+

+    def __init__(self, chose_rate, Ncp_type):

+        self.Ncp_type = Ncp_type

+        self.is_averagedFrames = 1

+        self.is_avgSlidingWindow = 1

+        self.is_avgSlidingWindow = 1

+        self.num_avg_frames = 10

+        self.symAmount = 14

+        

+        if (chose_rate == 1.4):

+            

+            self.sample_rate = 1.92*pow(10,6)

+            self.numRB = 6;

+            self.Nfft = 128

+            self.analysis_frame_len = 1920

+            self.interp_freqSpectrum_lowLimit = -0.96*pow(10,6)

+            self.interp_freqSpectrum_upperLimit = 0.96*pow(10,6)

+            

+            if (Ncp_type == 1):

+                self.NCP1 = 10

+                self.NCP2 = 9

+            else:

+                self.NCP1 = 10

+                self.NCP2 = 9

+        

+        elif (chose_rate == 3):

+        

+            self.sample_rate = 3.84*pow(10,6)

+            self.numRB = 15;

+            self.Nfft = 256

+            self.analysis_frame_len = 3840

+            self.interp_freqSpectrum_lowLimit = -1.92*pow(10,6)

+            self.interp_freqSpectrum_upperLimit = 1.92*pow(10,6)

+            

+            if (Ncp_type == 1):

+                self.NCP1 = 20

+                self.NCP2 = 18

+            else:

+                self.NCP1 = 20

+                self.NCP2 = 18    

+        

+        elif (chose_rate == 5):

+        

+            self.sample_rate = 7.68*pow(10,6)

+            self.numRB = 25;

+            self.Nfft = 512

+            self.analysis_frame_len = 7680

+            self.interp_freqSpectrum_lowLimit = -3.84*pow(10,6)

+            self.interp_freqSpectrum_upperLimit = 3.84*pow(10,6)

+            

+            if (Ncp_type == 1):

+                self.NCP1 = 40

+                self.NCP2 = 36

+            else:

+                self.NCP1 = 40

+                self.NCP2 = 36

+        

+        elif (chose_rate == 10):

+        

+            self.sample_rate = 15.36*pow(10,6)

+            self.numRB = 50;

+            self.Nfft = 1024

+            self.analysis_frame_len = 15360

+            self.interp_freqSpectrum_lowLimit = -7.68*pow(10,6)

+            self.interp_freqSpectrum_upperLimit = 7.68*pow(10,6)

+            

+            if (Ncp_type == 1):

+                self.NCP1 = 80

+                self.NCP2 = 72

+            else:

+                self.NCP1 = 80

+                self.NCP2 = 72

+        

+        elif (chose_rate == 15): 

+        

+            #### FIX ME ####

+            self.sample_rate = 23.04*pow(10,6)

+            self.numRB = 75;

+            self.Nfft = 2048

+            self.analysis_frame_len = 1920

+            self.interp_freqSpectrum_lowLimit = -0.96*pow(10,6)

+            self.interp_freqSpectrum_upperLimit = 0.96*pow(10,6)

+            

+            if (Ncp_type == 1):

+                self.NCP1 = 160

+                self.NCP2 = 144

+            else:

+                self.NCP1 = 160

+                self.NCP2 = 144

+            #### FIX ME ####

+        

+        elif (chose_rate == 20):

+        

+            self.sample_rate = 30.72*pow(10,6)

+            self.numRB = 100;

+            self.Nfft = 2048

+            self.analysis_frame_len = 30720

+            self.interp_freqSpectrum_lowLimit = -15.36*pow(10,6)

+            self.interp_freqSpectrum_upperLimit = 15.36*pow(10,6)

+            

+            if (Ncp_type == 1):

+                self.NCP1 = 160

+                self.NCP2 = 144

+            else:

+                self.NCP1 = 160

+                self.NCP2 = 144

+        

+        else:

+            sys.exit('Invalid LTE rate')

+          #error('Invalid LTE rate');

+        

+        self.sample_time = 1/    self.sample_rate;

+        self.PSS_FFT_size = self.Nfft
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/IQ_Plots.py b/mcu/tools/IQ_Analyzer/src/IQ_Plots.py
new file mode 100644
index 0000000..575aa74
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/IQ_Plots.py
@@ -0,0 +1,672 @@
+import numpy as np

+import matplotlib.pyplot as plt

+from mpl_toolkits.axes_grid1 import make_axes_locatable

+from matplotlib.ticker import MultipleLocator

+

+from pylab import *

+ 

+######################################################################################

+# ----------------------------------- NR PLOTTING (B) -------------------------------#

+######################################################################################

+class NR_Plot:

+    def __init__(self, params):

+        self.rows = 2

+        self.cols = 3

+        self.fig = plt.figure(figsize=(20,10),dpi=80)

+        self.fig.set_facecolor('black')

+        

+        self.params = params

+        self.ssb_idx = 0

+                

+    def timedomain_plot(self, timedomain_IQ):

+        time_vec = (1/self.params.sample_rate)*np.arange(len(timedomain_IQ))

+        ax = plt.subplot(self.rows, self.cols, 1)

+        ax.set_facecolor('black')

+        ax.set_title("Time domain IQ", fontsize=16, color='white')

+        ax.plot(time_vec,np.real(timedomain_IQ), 'cyan', ms=0.)

+        ax.plot(time_vec,np.imag(timedomain_IQ), 'purple', ms=0.)

+        ax.yaxis.grid(True, linestyle=':', which='major', color='black',alpha=1.0)

+        ax.xaxis.grid(True, linestyle=':', which='major', color='black',alpha=1.0)

+        ax.spines['bottom'].set_color('white')

+        ax.spines['top'].set_color('white') 

+        ax.spines['right'].set_color('white')

+        ax.spines['left'].set_color('white')

+        ax.tick_params(axis='x', colors='white')

+        ax.tick_params(axis='y', colors='white')    

+        ax.set_xlim(time_vec[0], time_vec[-1])

+        ymin, ymax = ax.get_ylim()

+        ax.set_xlabel('Time progression (ms)', color='white')

+        ax.set_ylabel('Amplitude', color='white')      

+

+        legend = ax.legend(('I-component','Q-component'), loc='upper center', bbox_to_anchor=(0.5, 1.0),ncol='2')

+        frame = legend.get_frame()

+        frame.set_facecolor('black')

+        frame.set_edgecolor('white')

+        for text in legend.get_texts():

+            plt.setp(text, color = 'white')

+        plt.setp(text, fontsize='12')           

+        

+    def freqdomain_plot(self, timedomain_IQ):

+        Accum_freqSpectrum_IQ_shifted_main = [0]* len(timedomain_IQ)

+        

+        # Frequency Span vector

+        Fs = self.params.sample_rate

+        N = len(timedomain_IQ)

+        dF = Fs/N     

+        Accum_slidingWinMat_freqSpectrum_IQ_shifted_main = np.zeros((len(timedomain_IQ), self.params.num_avg_frames))                 

+          

+        f_vec = np.arange(-Fs/2,Fs/2,dF)

+    

+        ax = plt.subplot(self.rows, self.cols, 4) 

+        ax.set_facecolor('black')

+        ax.yaxis.grid(True, linestyle=':', which='major', color='white',alpha=1.0)

+        ax.xaxis.grid(True, linestyle=':', which='major', color='white',alpha=1.0)

+        ax.spines['bottom'].set_color('white')

+        ax.spines['top'].set_color('white') 

+        ax.spines['right'].set_color('white')

+        ax.spines['left'].set_color('white')

+        ax.tick_params(axis='x', colors='white')

+        ax.tick_params(axis='y', colors='white')

+        ax.set_title('Frequency Spectrum',fontsize=14, color='white')

+        ax.set_xlabel('Frequency (MHz)',fontsize=11, color='white')

+        ax.set_ylabel('Power (dB)',fontsize=11, color='white')              

+    

+        # Compute FFT - Freq. Spectrum

+        freqSpectrum_IQ_main = (1.0 / self.params.analysis_frame_len) * np.fft.fft(timedomain_IQ)

+                

+        # Center Freq. Spectrum at 0 Hz

+        freqSpectrum_IQ_shifted_main = np.fft.fftshift(freqSpectrum_IQ_main)

+        

+        if (self.params.is_averagedFrames):    #  Reduce variance of a signal

+            if (self.params.is_avgSlidingWindow):

+            # MAIN

+                Accum_slidingWinMat_freqSpectrum_IQ_shifted_main[:,1:] = Accum_slidingWinMat_freqSpectrum_IQ_shifted_main[:,:-1] 

+                Accum_slidingWinMat_freqSpectrum_IQ_shifted_main[:,0] = np.absolute(freqSpectrum_IQ_shifted_main)

+                Accum_slidingWinVec_freqSpectrum_IQ_shifted_main = (1.0 / self.params.num_avg_frames) * Accum_slidingWinMat_freqSpectrum_IQ_shifted_main.sum(axis=1) 

+                

+                ax.plot(f_vec, 20.0*np.log10(np.absolute(freqSpectrum_IQ_shifted_main)), 'red',

+                        f_vec, 20.0*np.log10(Accum_slidingWinVec_freqSpectrum_IQ_shifted_main), 'orange')            

+                legendNames = ['Shifted at center freq', 'Avg sliding window']

+            else:

+                # MAIN/DIV

+                Accum_freqSpectrum_IQ_shifted_main = self.Accum_freqSpectrum_IQ_shifted_main + np.absolute(freqSpectrum_IQ_shifted_main)

+                

+                ax.plot(self.f_vec, 20.0*np.log10(np.absolute(freqSpectrum_IQ_shifted_main)), 'red',

+                          self.f_vec, 20.0*np.log10(Accum_freqSpectrum_IQ_shifted_main/frame_counter), 'orange') 

+                

+                legendNames = ['Main', 'Avg']

+        else:

+            ax.plot(self.f_vec, 20.0*np.log10(np.absolute(freqSpectrum_IQ_shifted_main)), 'y')

+            legendNames = ['Main']    

+            

+        legend = ax.legend((legendNames), loc=1, bbox_to_anchor=(0.5, 1.0), borderaxespad=0.)

+        frame = legend.get_frame()

+        frame.set_facecolor('black')

+        frame.set_edgecolor('white')

+        for text in legend.get_texts():

+            plt.setp(text, color = 'w')

+        plt.setp(text, fontsize='small')

+        

+        ylimit = np.max(np.ceil(np.absolute(20.0*np.log10(np.absolute(freqSpectrum_IQ_shifted_main)))))

+        ax.set_ylim([-150, ylimit+10])

+        ax.set_xlim(self.params.interp_freqSpectrum_lowLimit, self.params.interp_freqSpectrum_upperLimit)

+        ax.yaxis.grid(True, linestyle=':', which='major', color='white',alpha=1.0)

+        ax.xaxis.grid(True, linestyle=':', which='major', color='white',alpha=1.0)    

+        ax.set_title('Frequency Spectrum',fontsize=14, color='white')

+        ax.set_xlabel('Frequency (Hz)',fontsize=11, color='white')

+        ax.set_ylabel('Power (dB)',fontsize=11, color='white')        

+        

+    def ss_plot(self, ss_results):

+    

+        # ------------------ PSS PLOT PROCESSING INIT (B) ------------------

+        PSS_corr_magSQR_output_frame_main = ss_results.PSS_corr_magSQR_output_frame_main  # Correlation result

+        pss_time = len(PSS_corr_magSQR_output_frame_main)        

+        PSS_time_vec_tot = (1/self.params.sample_rate)*np.arange(pss_time)

+        # ------------------ PSS PLOT PROCESSING INIT (E) ------------------

+        

+        # ------------------ SSS PLOT PROCESSING INIT (B) ------------------ 

+        SSS_corr_magSQR_output_frame_main = ss_results.SSS_corr_magSQR_output_frame_main       

+        sss_time = len(SSS_corr_magSQR_output_frame_main)

+        SSS_time_vec_tot = (1/self.params.sample_rate)*np.arange(sss_time)

+        # ------------------ SSS PLOT PROCESSING INIT (E) ------------------

+    

+        ax = plt.subplot(self.rows, self.cols, 2)    

+        ax.set_facecolor('black')

+        ax.yaxis.grid(True, linestyle=':', which='major', color='black',alpha=1.0)

+        ax.xaxis.grid(True, linestyle=':', which='major', color='black',alpha=1.0)

+        ax.spines['bottom'].set_color('white')

+        ax.spines['top'].set_color('white') 

+        ax.spines['right'].set_color('white')

+        ax.spines['left'].set_color('white')

+        ax.tick_params(axis='x', colors='white')

+        ax.tick_params(axis='y', colors='white')           

+        

+        ax.plot(PSS_time_vec_tot, PSS_corr_magSQR_output_frame_main,'-', ms=3.0)       

+        ax.plot(SSS_time_vec_tot, SSS_corr_magSQR_output_frame_main,'.-', color='orangered', ms=2.0)       

+        

+        ax.set_xlim([PSS_time_vec_tot[0], PSS_time_vec_tot[-1]])

+        ax.set_ylim([0.0, 1.0])

+        ax.xaxis.grid(True, linestyle=':', which='major', color='black',alpha=0.5)  

+        

+        legend = ax.legend(('PSS', 'SSS'), loc='upper center', bbox_to_anchor=(0.5, 1.0),ncol='2', fontsize = 'large')

+        frame = legend.get_frame()

+        frame.set_facecolor('white')

+        frame.set_edgecolor('black')

+        for text in legend.get_texts():

+            plt.setp(text, color = 'black')

+        plt.setp(text, fontsize='large')

+            

+        ax.set_xlim([PSS_time_vec_tot[0], PSS_time_vec_tot[-1]])

+        ax.set_ylim([0.0, 1.0])

+        ax.xaxis.grid(True, linestyle=':', which='major', color='black',alpha=0.5)  

+                

+        ax.set_title('PSS/SSS Correlation Outputs',fontsize=14, color='white')

+        ax.set_xlabel('Time progression (sec.)',fontsize=14, color='white')

+        ax.set_ylabel('Amplitude',fontsize=14, color='white')    

+        

+    def resourceGrid_plot(self, half_frame2D_FD_occupiedRB):

+        sizeRB = 12

+        Y = np.arange(0, sizeRB*self.params.numRB)

+        X = np.arange(0, self.params.symAmount*5)

+        X, Y = np.meshgrid(X, Y)

+        

+        symAmount = self.params.symAmount

+        symAmount_5ms = 5*symAmount 

+        

+        ax = plt.subplot(self.rows, self.cols, 3)   

+        ax.set_facecolor('black')

+        ax.spines['bottom'].set_color('white')

+        ax.spines['top'].set_color('white') 

+        ax.spines['right'].set_color('white')

+        ax.spines['left'].set_color('white')

+        ax.tick_params(axis='x', colors='white')

+        ax.tick_params(axis='y', colors='white')

+        colorbar_divider = make_axes_locatable(ax)

+        cb_axes = colorbar_divider.append_axes("right", size="5%", pad=1.0)

+        ax.set_title('Five Subframes Resource Grid (Amplitude)',fontsize=14, color='white')

+        ax.set_xlabel('OFDM symbol index',fontsize=12, color='white')

+        ax.set_ylabel('Subcarrier index',fontsize=12, color='white')    

+        startSym = 0

+        

+        ax.cla()

+        Z = np.absolute(half_frame2D_FD_occupiedRB) 

+        im = ax.imshow(Z,

+                                                   interpolation='nearest',

+                                                   cmap="nipy_spectral",

+                                                   aspect='auto',

+                                                   origin="lower",

+                                                   vmin=0.0, vmax=20.0)

+               

+        major_ticks = np.arange(-0.5, symAmount_5ms+1, symAmount)                                              

+        minor_ticks = np.arange(-0.5, symAmount_5ms+1, 1)

+        ax.set_xticks(major_ticks)

+        ax.set_xticks(minor_ticks, minor=True)

+        ax.set_xticklabels(np.arange(startSym, symAmount_5ms+startSym+1, symAmount), fontsize=12)

+                    

+        ax.xaxis.grid(b=True, linestyle='-', which='major', color='black',alpha=1.0)

+        ax.xaxis.grid(b=True, linestyle=':', which='minor', color='black',alpha=0.5)                    

+        ax.set_xlim([-0.5, symAmount_5ms-0.5])        

+        ax.set_ylim([-0.5, 240-0.5])

+                    

+        ax.set_title('Five Subframes Resource Grid (Amplitude)',fontsize=14, color='white')

+        ax.set_xlabel('OFDM symbol index', fontsize=12, color='white')

+        ax.set_ylabel('Subcarrier index', fontsize=12, color='white')

+                    

+        grid_colorbar = plt.colorbar(im, cax=cb_axes, ticks=MultipleLocator(1.0), format="%.1f")

+        grid_colorbar_obj = plt.getp(grid_colorbar.ax.axes, 'yticklabels')

+        plt.setp(grid_colorbar_obj, color='white')        

+ 

+        

+    def constellation(self, pbchSymbols, detected_PBCH, place):

+        

+        ax = plt.subplot(self.rows, self.cols, place)   

+            

+        ax.set_facecolor('black')

+        ax.spines['bottom'].set_color('white')

+        ax.spines['top'].set_color('white') 

+        ax.spines['right'].set_color('white')

+        ax.spines['left'].set_color('white')

+        ax.tick_params(axis='x', colors='white')

+        ax.tick_params(axis='y', colors='white')        

+        ax.set_xlabel('In-phase',fontsize=10, color='white')

+        ax.set_ylabel('Quadrature-phase',fontsize=10, color='white')

+        colors = ['red', 'green', 'blue', 'white', 'magenta','orange','cyan','pink', 'red', 'green', 'blue', 'white', 'magenta','orange','cyan','pink']

+        

+        idx_first_PBCHsym = 0

+        idx_last_PBCHsym = 432                

+        for i in xrange(detected_PBCH):

+            line1 = ax.scatter(np.real(pbchSymbols[idx_first_PBCHsym:idx_last_PBCHsym]),np.imag(pbchSymbols[idx_first_PBCHsym:idx_last_PBCHsym]),

+                              color=colors[i],label='PBCH-QPSK'+str(self.ssb_idx),s=10,facecolors='none')    

+            idx_first_PBCHsym = idx_last_PBCHsym

+            idx_last_PBCHsym = idx_first_PBCHsym + 432

+            self.ssb_idx = self.ssb_idx + 1

+

+        if len(pbchSymbols) == 0:

+            return 0

+        else:

+            limit = np.max(np.ceil(np.absolute(pbchSymbols)))

+            ax.set_xlim([-limit, limit])

+            ax.set_ylim([-limit, limit])

+            

+            leg = ax.legend(loc='upper left', fancybox=True, shadow=True)

+            leg.get_frame().set_alpha(0.4)

+    

+            lines = [line1]

+            lined = dict()

+            for legline, origline in zip(leg.get_lines(), lines):

+                legline.set_picker(True)  # 5 pts tolerance

+                lined[legline] = origline

+

+

+    def pbchDMRS_plot(self, pbchDMRS_results, amount_of_pbchDMRS):

+

+        p = plt.figure(figsize=(10,6), facecolor='black') 

+        p.suptitle("PBCH DM-RS correlations (frequency domain)", fontsize = 'large', color='white')   

+

+        if amount_of_pbchDMRS > 8:

+            plotting_count = 8

+        else:

+            plotting_count = amount_of_pbchDMRS

+

+        max_ssb_candidates = 8

+        corr_result_length = 144

+        # init            

+        start = 0

+        end = 144                        

+        for j in xrange(1, plotting_count+1):       

+            s = plt.subplot(2, 4, j, facecolor='black') 

+            plt.subplots_adjust(hspace=0.5)              

+                          

+            maxVal = [] # Max values of correlation results

+            for i in xrange(max_ssb_candidates):

+                dmrsCorr = pbchDMRS_results[start:end]

+                dmrsMaxIdx = np.argmax(dmrsCorr)

+                peakVal = dmrsCorr[dmrsMaxIdx]

+                maxVal.append(peakVal)

+    

+                s.set_xlabel('SS block index', fontsize=14, color='white')

+                s.set_ylabel('Amplitude', fontsize=14, color='white')

+                s.set_ylim(0.0, 0.8)

+                s.tick_params(axis='x', colors='white')

+                s.tick_params(axis='y', colors='white')

+                s.spines['bottom'].set_color('white')

+                s.spines['top'].set_color('white') 

+                s.spines['right'].set_color('white')

+                s.spines['left'].set_color('white')                

+    

+                start = end

+                end = start + corr_result_length   

+                

+            x = [0, 1, 2, 3, 4, 5, 6, 7]

+            markerline, stemlines, baseline = stem(x, [maxVal[0], maxVal[1], maxVal[2], maxVal[3], maxVal[4], maxVal[5], maxVal[6], maxVal[7]], '-')    

+            setp(markerline, 'markerfacecolor', 'b')

+            p.show()

+

+

+######################################################################################

+# ----------------------------------- NR PLOTTING (B) -------------------------------#

+######################################################################################

+            

+######################################################################################

+# --------------------------------- LTE PLOTTING (B) --------------------------------#

+######################################################################################

+

+class FreqDomainPlots:

+    def __init__ (self, params, pos):    

+        

+        self.Accum_freqSpectrum_IQ_shifted_main = [0] * params.analysis_frame_len

+        

+        # Frequency Span vector

+        Fs = params.sample_rate

+        N = params.analysis_frame_len

+        dF = Fs/N     

+        self.Accum_slidingWinMat_freqSpectrum_IQ_shifted_main = np.zeros( (params.analysis_frame_len,params.num_avg_frames) )                 

+          

+        self.f_vec = np.arange(-Fs/2,Fs/2,dF)

+    

+        self.params = params

+        self.ax_m = plt.subplot(pos)

+        self.ax_m.set_facecolor('black')

+        self.ax_m.yaxis.grid(True, linestyle=':', which='major', color='white',alpha=1.0)

+        self.ax_m.xaxis.grid(True, linestyle=':', which='major', color='white',alpha=1.0)

+        self.ax_m.spines['bottom'].set_color('white')

+        self.ax_m.spines['top'].set_color('white') 

+        self.ax_m.spines['right'].set_color('white')

+        self.ax_m.spines['left'].set_color('white')

+        self.ax_m.tick_params(axis='x', colors='white')

+        self.ax_m.tick_params(axis='y', colors='white')

+        self.ax_m.set_title('Frequency Spectrum',fontsize=14, color='white')

+        self.ax_m.set_xlabel('Frequency (MHz)',fontsize=11, color='white')

+        self.ax_m.set_ylabel('Power (dB)',fontsize=11, color='white')

+

+    def process(self, IQ_frame_main_norm):

+    

+        # Compute FFT - Freq. Spectrum

+        freqSpectrum_IQ_main = (1.0 / self.params.analysis_frame_len) * np.fft.fft(IQ_frame_main_norm)

+                

+        # Center Freq. Spectrum at 0 Hz

+        freqSpectrum_IQ_shifted_main = np.fft.fftshift(freqSpectrum_IQ_main)

+        

+        self.ax_m.cla()        

+        if (self.params.is_averagedFrames):    #  Reduce variance of a signal

+            if (self.params.is_avgSlidingWindow):

+            # MAIN

+                self.Accum_slidingWinMat_freqSpectrum_IQ_shifted_main[:,1:] = self.Accum_slidingWinMat_freqSpectrum_IQ_shifted_main[:,:-1] 

+                self.Accum_slidingWinMat_freqSpectrum_IQ_shifted_main[:,0] = np.absolute(freqSpectrum_IQ_shifted_main)

+                self.Accum_slidingWinVec_freqSpectrum_IQ_shifted_main = (1.0 / self.params.num_avg_frames) * self.Accum_slidingWinMat_freqSpectrum_IQ_shifted_main.sum(axis=1) 

+                

+                self.ax_m.plot(self.f_vec, 20.0*np.log10(np.absolute(freqSpectrum_IQ_shifted_main)), 'red',

+                        self.f_vec, 20.0*np.log10(self.Accum_slidingWinVec_freqSpectrum_IQ_shifted_main), 'orange')            

+                legendNames = ['Shifted at center freq', 'Avg sliding window']

+            else:

+                # MAIN/DIV

+                self.Accum_freqSpectrum_IQ_shifted_main = self.Accum_freqSpectrum_IQ_shifted_main + np.absolute(freqSpectrum_IQ_shifted_main)

+                

+                self.ax_m.plot(self.f_vec, 20.0*np.log10(np.absolute(freqSpectrum_IQ_shifted_main)), 'red',

+                          self.f_vec, 20.0*np.log10(self.Accum_freqSpectrum_IQ_shifted_main/frame_counter), 'orange') 

+                

+                legendNames = ['Main', 'Avg']

+        else:

+            self.ax_m.plot(self.f_vec, 20.0*np.log10(np.absolute(freqSpectrum_IQ_shifted_main)), 'y')

+            legendNames = ['Main']    

+             

+        legend = self.ax_m.legend((legendNames), loc=1, bbox_to_anchor=(0.5, 1.0), borderaxespad=0.)

+        frame = legend.get_frame()

+        frame.set_facecolor('black')

+        frame.set_edgecolor('white')

+        for text in legend.get_texts():

+            plt.setp(text, color = 'w')

+        plt.setp(text, fontsize='small')

+        

+        self.ax_m.set_ylim([-150, 0])

+        self.ax_m.set_xlim(self.params.interp_freqSpectrum_lowLimit, self.params.interp_freqSpectrum_upperLimit)

+        self.ax_m.yaxis.grid(True, linestyle=':', which='major', color='white',alpha=1.0)

+        self.ax_m.xaxis.grid(True, linestyle=':', which='major', color='white',alpha=1.0)    

+        self.ax_m.set_title('Frequency Spectrum',fontsize=14, color='white')

+        self.ax_m.set_xlabel('Frequency (Hz)',fontsize=11, color='white')

+        self.ax_m.set_ylabel('Power (dB)',fontsize=11, color='white')

+

+    def reset(self):

+        self.Accum_freqSpectrum_IQ_shifted_main = [0] * self.params.analysis_frame_len    

+

+class TimeDomainPlots:

+    def __init__ (self, params, pos):    

+        self.time_vec_10subframes = (1/params.sample_rate)*np.arange(10*params.analysis_frame_len)

+        self.ax_t_i_main = plt.subplot(pos)

+        self.ax_t_i_main.set_facecolor('black')

+        self.ax_t_i_main.yaxis.grid(True, linestyle=':', which='major', color='black',alpha=1.0)

+        self.ax_t_i_main.xaxis.grid(True, linestyle=':', which='major', color='black',alpha=1.0)

+        self.ax_t_i_main.spines['bottom'].set_color('white')

+        self.ax_t_i_main.spines['top'].set_color('white') 

+        self.ax_t_i_main.spines['right'].set_color('white')

+        self.ax_t_i_main.spines['left'].set_color('white')

+        self.ax_t_i_main.tick_params(axis='x', colors='white')

+        self.ax_t_i_main.tick_params(axis='y', colors='white')

+        self.ax_t_i_main.set_title('Time-Domain IQ Plot',fontsize=14, color='white')

+        self.ax_t_i_main.set_xlabel('Time progression (millisec.)',fontsize=14, color='white')

+        self.ax_t_i_main.set_ylabel('Amplitude',fontsize=14, color='white')

+

+    def process(self, IQ_full_frame_main):

+        

+        self.ax_t_i_main.cla()

+        i_frame = np.real(IQ_full_frame_main)

+        q_frame = np.imag(IQ_full_frame_main)

+        

+        maxValueIdx = np.argmax(i_frame) 

+        maxValue = i_frame[maxValueIdx]

+        self.ax_t_i_main.plot(1000*self.time_vec_10subframes, i_frame,'cyan', ms=0.5)

+        self.ax_t_i_main.plot(1000*self.time_vec_10subframes, q_frame,'purple', ms=0.5)

+        self.ax_t_i_main.set_xlim([self.time_vec_10subframes[0], self.time_vec_10subframes[-1]])

+        self.ax_t_i_main.set_ylim([-maxValue, maxValue])

+        self.ax_t_i_main.set_xticks(np.arange(0, 11))

+        self.ax_t_i_main.xaxis.grid(True, linestyle=':', which='major', color='white',alpha=0.5)

+        self.ax_t_i_main.set_title('Time-Domain IQ Plot', fontsize=14, color='white')

+        self.ax_t_i_main.set_xlabel('Time progression (millisec.)',fontsize=14, color='white')

+        self.ax_t_i_main.set_ylabel('Amplitude', fontsize=14, color='white')

+                

+        legend = self.ax_t_i_main.legend(('I-component','Q-component'), loc='upper center', bbox_to_anchor=(0.5, 1.0),ncol='2')

+        frame = legend.get_frame()

+        frame.set_facecolor('black')

+        frame.set_edgecolor('white')

+        for text in legend.get_texts():

+            plt.setp(text, color = 'white')

+        plt.setp(text, fontsize='12')    

+        

+class PssCorrPlots:

+    def __init__ (self, params, pos):    

+        self.params = params

+        self.frame_len = params.analysis_frame_len

+        self.ax_pss_main = plt.subplot(pos)

+        self.ax_pss_main.set_facecolor('black')

+        self.ax_pss_main.yaxis.grid(True, linestyle=':', which='major', color='white',alpha=1.0)

+        self.ax_pss_main.xaxis.grid(True, linestyle=':', which='major', color='white',alpha=1.0)

+        self.ax_pss_main.spines['bottom'].set_color('white')

+        self.ax_pss_main.spines['top'].set_color('white') 

+        self.ax_pss_main.spines['right'].set_color('white')

+        self.ax_pss_main.spines['left'].set_color('white')

+        self.ax_pss_main.tick_params(axis='x', colors='white')

+        self.ax_pss_main.tick_params(axis='y', colors='white')

+        self.ax_pss_main.set_title('PSS/SSS Correlation Outputs',fontsize=14, color='white')

+        self.ax_pss_main.set_xlabel('Time progression (millisec.)',fontsize=14, color='white')

+        self.ax_pss_main.set_ylabel('Amplitude',fontsize=14, color='white')

+        self.params = params    

+                       

+    # LTE 

+    def process(self, pss):    

+        

+        PSS_time_vec_tot = (1/self.params.sample_rate)*np.arange(self.params.analysis_frame_len*10)

+        PSS_corr_magSQR_output_frame_main = pss.PSS_corr_magSQR_output_frame_main

+        PSS_peak_detected = pss.PSS_peak_detected 

+        PSS_Subframe_max_idx = pss.PSS_Subframe_max_idx 

+        PSS_Subframe_max_val = pss.PSS_Subframe_max_val

+

+        SSS_corr_magSQR_output_frame_main = pss.SSS_corr_magSQR_output_frame_main

+        SSS_peak_detected = pss.SSS_peak_detected 

+        SSS_Subframe_max_idx = pss.SSS_Subframe_max_idx 

+        SSS_Subframe_max_val = pss.SSS_Subframe_max_val

+

+        self.ax_pss_main.cla()

+        self.ax_pss_main.plot(1000*PSS_time_vec_tot,PSS_corr_magSQR_output_frame_main,'.-', color='green', ms=2.0)

+        self.ax_pss_main.plot(1000*PSS_time_vec_tot,SSS_corr_magSQR_output_frame_main,'.-', color='lightgreen', ms=2.0)

+                    

+        if (PSS_peak_detected == 1):

+            peak_idx = (1/self.params.sample_rate)*(9*self.params.analysis_frame_len+PSS_Subframe_max_idx)

+            self.ax_pss_main.plot(1000*peak_idx,PSS_Subframe_max_val,marker="o", color='red', ms=10.0)

+        if (SSS_peak_detected == 1):

+            peak_idx = (1/self.params.sample_rate)*(9*self.params.analysis_frame_len+SSS_Subframe_max_idx)

+            self.ax_pss_main.plot(1000*peak_idx,SSS_Subframe_max_val,marker="o", color='pink', ms=10.0)

+                    

+        self.ax_pss_main.set_xlim([PSS_time_vec_tot[0], PSS_time_vec_tot[-1]])

+        self.ax_pss_main.set_yticklabels(fontsize=20) 

+        

+        self.ax_pss_main.set_ylim([0.0,0.5])

+        self.ax_pss_main.set_xticks([0.0,0.0010666,0.0021332,0.0031998,0.00426641,0.00533301,0.00639961,0.00746621,0.00853281,0.00959941,0.01066602])

+        

+        self.ax_pss_main.xaxis.grid(True, linestyle=':', which='major', color='white',alpha=0.5)

+                

+        self.ax_pss_main.set_title('PSS/SSS Correlation Outputs',fontsize=14, color='white')

+        self.ax_pss_main.set_xlabel('Time progression (millisec.)',fontsize=20, color='white')

+        self.ax_pss_main.set_ylabel('Amplitude',fontsize=20, color='white') 

+       

+class ResourceGrid:

+    def __init__ (self, params, pos):

+        sizeRB = 12

+        Y = np.arange(0, sizeRB*params.numRB)

+        X = np.arange(0, params.symAmount*5)

+        X, Y = np.meshgrid(X, Y)

+        

+        self.symAmount = params.symAmount

+        self.symAmount_5ms = 5*self.symAmount

+        

+        self.ax_5msFD_grid_main = plt.subplot(pos)

+        self.ax_5msFD_grid_main.set_facecolor('black')

+        self.ax_5msFD_grid_main.spines['bottom'].set_color('white')

+        self.ax_5msFD_grid_main.spines['top'].set_color('white') 

+        self.ax_5msFD_grid_main.spines['right'].set_color('white')

+        self.ax_5msFD_grid_main.spines['left'].set_color('white')

+        self.ax_5msFD_grid_main.tick_params(axis='x', colors='white')

+        self.ax_5msFD_grid_main.tick_params(axis='y', colors='white')

+        self.colorbar_divider = make_axes_locatable(self.ax_5msFD_grid_main)

+        self.cb_axes = self.colorbar_divider.append_axes("right", size="5%", pad=1.0)

+        self.ax_5msFD_grid_main.set_title('Five Subframes Resource Grid (Amplitude)',fontsize=14, color='white')

+        self.ax_5msFD_grid_main.set_xlabel('OFDM symbol index',fontsize=12, color='white')

+        self.ax_5msFD_grid_main.set_ylabel('Subcarrier index',fontsize=12, color='white')

+        self.startSym = 0

+

+    def process(self, half_frame2D_FD_occupiedRB):        

+        self.ax_5msFD_grid_main.cla()

+        Z = np.absolute(half_frame2D_FD_occupiedRB) 

+        im = self.ax_5msFD_grid_main.imshow(Z,

+                                                   interpolation='nearest',

+                                                   cmap="nipy_spectral",

+                                                   aspect='auto',

+                                                   origin="lower",

+                                                   vmin=0.0, vmax=20.0)

+        

+        self.startSym = 0

+        

+        major_ticks = np.arange(-0.5, self.symAmount_5ms+1, self.symAmount)                                              

+        minor_ticks = np.arange(-0.5, self.symAmount_5ms+1, 1)

+        self.ax_5msFD_grid_main.set_xticks(major_ticks)

+        self.ax_5msFD_grid_main.set_xticks(minor_ticks, minor=True)

+        self.ax_5msFD_grid_main.set_xticklabels(np.arange(self.startSym, self.symAmount_5ms+self.startSym+1, self.symAmount), fontsize=12)

+                    

+        self.ax_5msFD_grid_main.xaxis.grid(b=True, linestyle='-', which='major', color='black',alpha=1.0)

+        self.ax_5msFD_grid_main.xaxis.grid(b=True, linestyle=':', which='minor', color='black',alpha=0.5)                    

+        self.ax_5msFD_grid_main.set_xlim([-0.5, self.symAmount_5ms-0.5])        

+        self.ax_5msFD_grid_main.set_ylim([-0.5, 240-0.5])

+                    

+        self.ax_5msFD_grid_main.set_title('Five Subframes Resource Grid (Amplitude)',fontsize=14, color='white')

+        self.ax_5msFD_grid_main.set_xlabel('OFDM symbol index', fontsize=12, color='white')

+        self.ax_5msFD_grid_main.set_ylabel('Subcarrier index', fontsize=12, color='white')

+                    

+        self.grid_colorbar = plt.colorbar(im, cax=self.cb_axes, ticks=MultipleLocator(1.0), format="%.1f")

+        self.grid_colorbar_obj = plt.getp(self.grid_colorbar.ax.axes, 'yticklabels')

+        plt.setp(self.grid_colorbar_obj, color='white')

+

+class PilotsPlots:

+    def __init__ (self, params, pos, idx):

+        self.idx = idx

+        self.ax_pilots_sf0_main = plt.subplot(pos)

+        self.ax_pilots_sf0_main.set_facecolor('black')

+        self.ax_pilots_sf0_main.spines['bottom'].set_color('white')

+        self.ax_pilots_sf0_main.spines['top'].set_color('white') 

+        self.ax_pilots_sf0_main.spines['right'].set_color('white')

+        self.ax_pilots_sf0_main.spines['left'].set_color('white')

+        self.ax_pilots_sf0_main.tick_params(axis='x', colors='white')

+        self.ax_pilots_sf0_main.tick_params(axis='y', colors='white')

+            

+        self.ax_pilots_sf0_main.set_title('IQ Raw Pilots (S'+ str(self.idx) + ')',fontsize=12, color='white')

+        self.ax_pilots_sf0_main.set_xlabel('In-phase',fontsize=10, color='white')

+        self.ax_pilots_sf0_main.set_ylabel('Quadrature',fontsize=10, color='white')

+        

+    def process(self,Pilots_5subFrames_RAW):

+        lim_vec = [-10.0,10.0]

+        colors_ = ['blue','green','red','cyan','yellow']

+        add_idx = self.idx  * 4

+                    

+        self.ax_pilots_sf0_main.cla()

+                    

+        for kk in xrange(4):    

+            self.ax_pilots_sf0_main.scatter(np.real(Pilots_5subFrames_RAW[:,add_idx+kk]),np.imag(Pilots_5subFrames_RAW[:,add_idx+kk]),color=colors_[kk], s=1.0)

+                        

+        self.ax_pilots_sf0_main.set_aspect('equal')

+        self.ax_pilots_sf0_main.set_xlim(lim_vec)

+        self.ax_pilots_sf0_main.set_ylim(lim_vec)

+        self.ax_pilots_sf0_main.set_title('IQ Raw Pilots (S'+ str(self.idx) + ')',fontsize=12, color='white')

+        self.ax_pilots_sf0_main.set_xlabel('In-phase',fontsize=10, color='white')

+        self.ax_pilots_sf0_main.set_ylabel('Quadrature',fontsize=10, color='white')

+

+class PilotsPhasePlots:

+    def __init__ (self, params, pos, idx):

+        self.idx = idx

+        self.ax_pilots_phase_sf0_main = plt.subplot(pos)

+        self.ax_pilots_phase_sf0_main.set_facecolor('black')

+        self.ax_pilots_phase_sf0_main.spines['bottom'].set_color('white')

+        self.ax_pilots_phase_sf0_main.spines['top'].set_color('white') 

+        self.ax_pilots_phase_sf0_main.spines['right'].set_color('white')

+        self.ax_pilots_phase_sf0_main.spines['left'].set_color('white')

+        self.ax_pilots_phase_sf0_main.tick_params(axis='x', colors='white')

+        self.ax_pilots_phase_sf0_main.tick_params(axis='y', colors='white')

+            

+        self.ax_pilots_phase_sf0_main.set_title('Mag/Phase Pilots (S'+ str(self.idx) + ')',fontsize=12, color='white')

+        self.ax_pilots_phase_sf0_main.set_xlabel('In-phase',fontsize=10, color='white')

+        self.ax_pilots_phase_sf0_main.set_ylabel('Quadrature',fontsize=10, color='white')

+            

+    def process(self,CSRS_ChannelEst_RAW):

+        lim_vec = [-10.0,10.0]

+        colors_ = ['blue','green','red','cyan','yellow']

+        add_idx = self.idx * 4

+

+        self.ax_pilots_phase_sf0_main.cla()

+                    

+        for kk in xrange(0,4):    

+            self.ax_pilots_phase_sf0_main.scatter(np.real(CSRS_ChannelEst_RAW[:,add_idx+kk]),np.imag(CSRS_ChannelEst_RAW[:,add_idx+kk]),color=colors_[kk], s=1.0)    

+                

+        self.ax_pilots_phase_sf0_main.set_aspect('equal')

+        self.ax_pilots_phase_sf0_main.set_xlim(lim_vec)

+        self.ax_pilots_phase_sf0_main.set_ylim(lim_vec)

+        self.ax_pilots_phase_sf0_main.set_title('Mag/Phase Pilots (S'+ str(self.idx) + ')',fontsize=12, color='white')

+        self.ax_pilots_phase_sf0_main.set_xlabel('In-phase',fontsize=10, color='white')

+        self.ax_pilots_phase_sf0_main.set_ylabel('Quadrature',fontsize=10, color='white')

+        

+class CompensatedDataPlots:

+    def __init__ (self, params, pos, idx):

+        self.idx = idx

+        self.ax_data_sf0 = plt.subplot(pos)

+        self.ax_data_sf0.set_facecolor('black')

+        self.ax_data_sf0.spines['bottom'].set_color('white')

+        self.ax_data_sf0.spines['top'].set_color('white') 

+        self.ax_data_sf0.spines['right'].set_color('white')

+        self.ax_data_sf0.spines['left'].set_color('white')

+        self.ax_data_sf0.tick_params(axis='x', colors='white')

+        self.ax_data_sf0.tick_params(axis='y', colors='white')

+        if self.idx == 0:

+            self.ax_data_sf0.set_title('IQ Scatter (Subframe 0)',fontsize=14, color='white')                    

+            self.legend = self.ax_data_sf0.legend(('Slot 0','Slot 1'),loc='upper left', bbox_to_anchor=(1.0, 1.0))

+            self.frame = self.legend.get_frame()

+            self.frame.set_facecolor('black')

+            self.frame.set_edgecolor('white')

+            for text in self.legend.get_texts():

+                    plt.setp(text, color = 'w')

+        else:            

+            self.ax_data_sf0.set_title('IQ Scatter (Subframe ' + str(idx) +')',fontsize=14, color='white')

+        self.ax_data_sf0.set_xlabel('In-phase',fontsize=11, color='white')

+        self.ax_data_sf0.set_ylabel('Quadrature',fontsize=11, color='white')

+        

+    def process (self, full_phaseComp_mat):

+        ofdm_sym = [1,2,3,5,6]

+        lim_vec = [-2.0,2.0]

+                    

+        self.ax_data_sf0.cla()

+        self.ax_data_sf0.cla()

+        self.ax_data_sf0.cla()

+                    

+        for kk in xrange(5):

+            _offset_ = self.idx * 14

+            self.ax_data_sf0.scatter(np.real(full_phaseComp_mat[:,ofdm_sym[kk]+_offset_]),np.imag(full_phaseComp_mat[:,ofdm_sym[kk]+_offset_]),color='magenta', s=1.0)

+            _offset_ = _offset_ + 7

+            self.ax_data_sf0.scatter(np.real(full_phaseComp_mat[:,ofdm_sym[kk]+_offset_]),np.imag(full_phaseComp_mat[:,ofdm_sym[kk]+_offset_]),color='yellow', s=1.0)

+                        

+        self.ax_data_sf0.set_aspect('equal')

+        self.ax_data_sf0.set_xlim(lim_vec)

+        self.ax_data_sf0.set_ylim(lim_vec)

+

+        self.ax_data_sf0.set_xlabel('In-phase',fontsize=11, color='white')

+        self.ax_data_sf0.set_ylabel('Quadrature',fontsize=11, color='white')

+

+        if self.idx == 0:

+            self.ax_data_sf0.set_title('IQ Scatter (Subframe 0)',fontsize=14, color='white')                    

+            self.legend = self.ax_data_sf0.legend(('Slot 0','Slot 1'),loc='upper left', bbox_to_anchor=(1.0, 1.0))

+            self.frame = self.legend.get_frame()

+            self.frame.set_facecolor('black')

+            self.frame.set_edgecolor('white')

+            for text in self.legend.get_texts():

+                    plt.setp(text, color = 'w')

+        else:

+            self.ax_data_sf0.set_title('Subframe ' + str(self.idx) ,fontsize=14, color='white')

+

+

+######################################################################################

+# ---------------------------------- LTE PLOTTING (E) -------------------------------#

+######################################################################################                            
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/IQ_Signals.py b/mcu/tools/IQ_Analyzer/src/IQ_Signals.py
new file mode 100644
index 0000000..2c24674
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/IQ_Signals.py
@@ -0,0 +1,191 @@
+import math

+import numpy as np

+

+class IqSignals:

+    def __init__(self, params, NidCell, is_TDD):

+        pi = math.pi

+

+        ## ------------------ PSS INIT (B) ------------------

+        print "\n"

+        print "Generating Primary Synchronization Signal....",

+

+        #Compute physical layer identity Nid2 in range [0:2]

+        Nid2 = NidCell % 3

+

+        #Identify root index from Nid2

+        if(Nid2 == 0):

+            u = 25

+        elif(Nid2 == 1):

+            u = 29

+        elif(Nid2 == 2):

+            u = 34

+            

+        self.PSS_TD_complex = [0] * 62

+        

+        # The PSS is a sequence of complex symbols, 62 symbols long

+        for n in xrange(0, 62):

+            

+            if n <= 30:    

+                phase_coeff = -pi*u*(n+1)*n/63.0     

+            else:        

+                phase_coeff = -pi*u*(n+1)*(n+2)/63.0

+          

+            self.PSS_TD_complex[n] = np.vectorize(complex)(math.cos(phase_coeff),math.sin(phase_coeff))   

+        

+        self.PSS_TD_complex_pad = [0] * 72

+        self.PSS_TD_complex_pad[5:67] = self.PSS_TD_complex

+        self.PSS_TD_complex_pad_fftSize_wShift = [0] * params.PSS_FFT_size

+        self.PSS_TD_complex_pad_fftSize_wShift[1:37] = self.PSS_TD_complex_pad[36:]

+        self.PSS_TD_complex_pad_fftSize_wShift[params.PSS_FFT_size-36:] = self.PSS_TD_complex_pad[:36]    

+        self.PSS_TD_complex_pad_fftSize_wShift_ifft = np.fft.ifft(self.PSS_TD_complex_pad_fftSize_wShift)

+        

+        #Define indices location of PSS in half-frame resource grid (TODO!)

+        

+        print "Done."

+        ## ------------------ PSS INIT (B) ------------------

+

+        ## See http://www.sharetechnote.com/html/Handbook_LTE_SSS.html < Matlab code for SSS Generation >

+        ## ------------------ SSS INIT (B) ------------------

+                #Compute physical layer cell-identity group Nid1 ( range LTE:[0:167], NR:[0:335])

+        print "Generating Secondary Synchronization Signal....",

+

+        #Compute physical layer cell-identity group Nid1 ( range [0:167] )

+        Nid1 = math.floor(NidCell/3.0)

+

+        # Define indices m0 and m1

+        q_prime = math.floor(Nid1/30.0)

+        q_ = math.floor( (Nid1 + 0.5*q_prime*(q_prime+1.0)) / 30.0 )

+        m_prime = Nid1 + q_*(q_+1)/2

+        m0 = int(m_prime % 31)

+        m1 = int((m0 + math.floor(m_prime/31.0) + 1.0) % 31)

+        

+        #print ['q_prime',q_prime,'q_',q_,'m_prime',m_prime,'m0',m0,'m1',m1]

+        

+        

+        # **** Generate sequences s0^(m0)[n] and s1^(m1)[n] ****

+        # Compute m-sequence s_tilde

+        x_s = [0] * 31

+        x_s[4] = 1

+        for i_ in xrange(0,26):

+            x_s[i_+5] = (x_s[i_+2] + x_s[i_]) % 2

+        

+        # Matlab: s_tilde  = 1 - 2*x_s

+        s_tilde = [1-2*x for x in x_s]

+        

+        # Compute s0_m0 and s1_m1 from s_tilde with different cyclic shifts

+        self.s0_ = [0] * 31

+        self.s1_ = [0] * 31

+        for i_ in xrange(0,31):

+            self.s0_[i_] = s_tilde[(i_ + m0) % 31] 

+            self.s1_[i_] = s_tilde[(i_ + m1) % 31]

+        

+        

+        # **** Generate scrambling sequences c0[n] and c1[n] ****

+        # Compute m-sequence c_tilde

+        x_c = [0] * 31

+        x_c[4] = 1

+        for i_ in xrange(0,26):

+            x_c[i_+5] = (x_c[i_+3] + x_c[i_]) % 2

+        

+        # Matlab: c_tilde  = 1 - 2*x_c

+        c_tilde = [1-2*x for x in x_c]

+        

+        # Compute c0_ and c1_ from c_tilde with different cyclic shifts

+        self.c0_ = [0] * 31

+        self.c1_ = [0] * 31

+        for i_ in xrange(0,31):

+            self.c0_[i_] = c_tilde[(i_ + Nid2) % 31] 

+            self.c1_[i_] = c_tilde[(i_ + Nid2 + 3) % 31]

+        

+        

+        # **** Generate scrambling sequences z1^(m0)[n] and z1^(m1)[n] ****

+        # Compute m-sequence z_tilde

+        x_z = [0] * 31

+        x_z[4] = 1

+        for i_ in xrange(0,26):

+            x_z[i_+5] = (x_z[i_+4] + x_z[i_+2] + x_z[i_+1] + x_z[i_]) % 2

+        

+        # Matlab: z_tilde  = 1 - 2*x_z

+        z_tilde = [1-2*x for x in x_z]

+        

+        # Compute z1_m0 and z1_m1 from z_tilde with different cyclic shifts

+        self.z1_m0 = [0] * 31

+        self.z1_m1 = [0] * 31

+        for i_ in xrange(0,31):

+            self.z1_m0[i_] = z_tilde[(i_ + (m0%8)) % 31] 

+            self.z1_m1[i_] = z_tilde[(i_ + (m1%8)) % 31]

+        

+        

+        #Define indices location of SSS in half-frame resource grid

+        self.SSS_k_index_start = 0 -31 + 12*params.numRB/2  

+        self.SSS_k_index_end = 61 -31 + 12*params.numRB/2  

+        

+        if (is_TDD == 1):

+            self.SSS_l_column = 13

+        else:

+            self.SSS_l_column = 5

+        

+        print "Done."

+        ## ------------------ SSS INIT (E) ------------------

+        

+        ########################################################################

+        # ------------------ SYNCHRONIZATION SIGNALS INIT (E) -----------------#

+        ########################################################################

+        

+        

+        

+        ################################################################################

+        # ------------------ CELL-SPECIFIC REFERENCE SIGNALS INIT (B) -----------------#

+        ################################################################################

+        print "Generating Cell-Specific Reference Signals....",

+        

+        N_c = 1600

+        NmaxRB = 110

+        seqLength = 4*NmaxRB

+        real_vec = np.zeros((2*NmaxRB,20*(params.Ncp_type+6)))

+        imag_vec = np.zeros((2*NmaxRB,20*(params.Ncp_type+6)))

+        self.CSRS_mat = np.vectorize(complex)(real_vec,imag_vec)

+        

+        for kk in xrange(0, 20):

+            

+            for ii in xrange(0,(params.Ncp_type+6)):

+                

+                c_init = 1024 * ( 7 * ( kk + 1 ) + 1 + ii ) * (2*NidCell+1) + 2*NidCell + params.Ncp_type

+                

+                #print c_init

+                 

+                # x_1 initialization

+                x_1 = [0] * (seqLength + N_c + 31)

+                x_1[0] = 1

+                x_2 = [0] * (seqLength + N_c + 31)

+                

+        #        # Initializes x_2 using c_init (converts to binary form)

+        #        i = 0;    

+        #        while (c_init >= 1):

+        #            x_2[i] = c_init % 2

+        #            c_init = math.floor(c_init / 2)

+        #            i = i +1

+                

+                # Initializes x_2 using c_init (converts to binary form)

+                i = 30;

+                while (c_init >= 1):

+                    x2_val = math.floor(c_init/math.pow(2,i))

+                    x_2[i] = x2_val

+                    c_init = c_init - x2_val*math.pow(2,i)

+                    i = i - 1

+                

+                # Generation of x_1[n] and x_2[n]    

+                for n in xrange(0,seqLength + N_c):

+                    x_1[n+31] = (x_1[n+3] + x_1[n]) % 2

+                    x_2[n+31] = (x_2[n+3] + x_2[n+2] + x_2[n+1] + x_2[n]) % 2    

+                

+                # Generation of Gold PN sequence: c_seq[n] = (x_1[n+Nc] + x_2[n+Nc])mod2

+                c_seq = np.zeros((seqLength,1))

+                for nn in xrange(0,seqLength):

+                    c_seq[nn] = (x_1[nn+N_c] + x_2[nn + N_c]) % 2

+        

+                # Generation of QPSK-based CSRS

+                ref_sig_seq = np.sqrt(0.5) * np.vectorize(complex)(1-2*c_seq[0:4*NmaxRB:2],1-2*c_seq[1:4*NmaxRB:2])

+                self.CSRS_mat[:,kk*(6+params.Ncp_type)+ii] = ref_sig_seq[:,0]

+        self.NmaxRB = NmaxRB

+        print "Done."

diff --git a/mcu/tools/IQ_Analyzer/src/IQ_Utils.py b/mcu/tools/IQ_Analyzer/src/IQ_Utils.py
new file mode 100644
index 0000000..7a6fb4b
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/IQ_Utils.py
@@ -0,0 +1,180 @@
+import math

+import numpy as np

+

+class L:

+    fw = None

+    @staticmethod

+    def logopen(f_path):

+        L.fw = open(f_path,"w")

+

+    @staticmethod    

+    def info(infostr):

+        print infostr

+        file_string = infostr + '\n'

+        L.fw.write(file_string)         

+    @staticmethod

+    def logclose():

+        L.fw.close()

+        L.fw = None

+

+def bi2de(binary):

+    decValue = int(binary, 2)

+    

+    return decValue

+

+def twos_comp(val, bits):

+    

+    """compute the 2's compliment of int value val"""

+    if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255

+        val = val - (1 << bits)        # compute negative value

+    return val        

+

+

+def readTimeInfoFile(time_file_path):

+    

+    print "\n"

+    print "Opening Time Info File...."

+    

+    # Determine number of time stamps in the file

+    fr_TIME = open(time_file_path,"r+")

+    time_file_line_count = 0

+    while True:

+        time_file_line_count = time_file_line_count + 1          

+        if fr_TIME.readline() == '':

+            time_file_line_count = time_file_line_count - 1

+            break

+    

+    # Read and store all time stamps 

+    fr_TIME = open(time_file_path,"r+")        

+    file_mat = [0 for y in range(time_file_line_count)]

+    for kk in xrange(0,time_file_line_count):

+        file_mat[kk] = fr_TIME.readline() 

+    

+    #Determine FRC capture time boundaries

+    startFRC = file_mat[0]

+    endingFRC = file_mat[-1]

+    

+    print "Time info file contains starting FRC:",

+    print str(int(math.floor(int(startFRC.partition(' ')[0],16) / 64))) + "(64us)"

+    print "Time info file contains ending FRC:",

+    print str(int(math.floor(int(endingFRC.partition(' ')[0],16) / 64))) + "(64us)"

+    

+    return file_mat

+

+

+

+def det_iqPairCount(inputTimeMat,frc_):

+    

+    frc_64 = frc_ * 64

+    

+    print "\n"

+    print "Input FRC:" + str(frc_) + "(64us)"

+    print "Identifying SIB PCC Packet Data from input FRC...."  

+         

+    #Covert inputFile FRC column to int

+    FRC_col_int = [0] * len(inputTimeMat)

+    for kk in xrange(0,len(inputTimeMat)):

+        line_string  = inputTimeMat[kk]

+        FRC_col_int[kk] = int(line_string.partition(' ')[0],16)

+    

+    #Find packet idx in Time Info File

+    min_FRC_idx = np.argmin([np.absolute(x-frc_64) for x in FRC_col_int])

+    

+    #Accumulate packed sizes until min_FRC_idx time row

+    line_add = 0

+    for jj in xrange(0,min_FRC_idx):

+        line_string = inputTimeMat[jj]

+        line_split = line_string.split()

+        line_add = line_add + int(line_split[3],16) 

+        #print [line_split[3], int(line_split[3],16), line_add]

+    

+    print "Found input FRC in packet with FRC_Time:" + str(FRC_col_int[min_FRC_idx]/64) +  "(64us) - {id: " + hex(FRC_col_int[min_FRC_idx]) + "}"      

+    #print [min_FRC_idx, FRC_col_int[min_FRC_idx], frc_64]

+    

+    line_split =  inputTimeMat[min_FRC_idx].split()

+    line_split_next = inputTimeMat[min_FRC_idx+1].split()

+    

+    packet_size = 4 * int(line_split[3],16)                    #Fetch packet size

+    packet_frc = int(line_split[0],16)                            #Fetch packet start

+    packet_frc_next  = int(line_split_next[0],16)        #Fetch next packet start

+    

+    frc_diff1 = frc_64 - packet_frc                                 #Determine start and input FRC difference

+    frc_diff2 = packet_frc_next - packet_frc                 #Determine start and next(packet) FRC difference

+    

+    t_diff_ratio = frc_diff1 / float(frc_diff2)

+    packet_fraction = int(t_diff_ratio * packet_size)

+    

+#    print [packet_frc, frc_64, frc_diff1]

+#    print [packet_frc, packet_frc_next, frc_diff2]    

+#    print [packet_size, t_diff_ratio, packet_fraction]

+    

+    iqPair_count = 4 * line_add + packet_fraction

+    return iqPair_count 

+

+

+def CFO_stats_print( inst_CFO, cumm_CFO, cfo_count):

+    

+    fw = L.fw

+    print ""

+    print "CFO Report (Instantaneous)"

+    print "--------------------------"

+    fw.write('\n')

+    fw.write("CFO Report (Instantaneous)")

+    fw.write("--------------------------")

+    fw.write('\n')

+    for kk in xrange(0,5):

+         

+        print "Subframe %d (Hz):" % kk,

+        string = "Subframe " + str(kk) +  " (Hz):"  

+        fw.write(string)

+        if (kk<4):    

+            v_ = inst_CFO[:,4*kk:4*kk+4]

+            print "{:10.4f} {:10.4f} {:10.4f} {:10.4f}".format(float(v_[:,0]),float(v_[:,1]),float(v_[:,2]),float(v_[:,3]))

+            string = "{:10.4f} {:10.4f} {:10.4f} {:10.4f}".format(float(v_[:,0]),float(v_[:,1]),float(v_[:,2]),float(v_[:,3]))

+            string = string + '\n'

+            fw.write(string)

+        else:

+            v_ = inst_CFO[:,4*kk:]

+            print "{:10.4f} {:10.4f} {:10.4f}".format(float(v_[:,0]),float(v_[:,1]),float(v_[:,2]))

+            string =  "{:10.4f} {:10.4f} {:10.4f}".format(float(v_[:,0]),float(v_[:,1]),float(v_[:,2]))

+            string = string + '\n'

+            fw.write(string)

+    print ""

+    

+    print ""

+    print "CFO Report (Avg)"

+    print "--------------------------"

+    fw.write('\n')

+    fw.write("CFO Report (Avg)")

+    fw.write("--------------------------")

+    fw.write('\n')

+    for kk in xrange(0,5):

+        print "Subframe %d (Hz): " % kk,

+        string = "Subframe " + str(kk) +  " (Hz):"  

+        fw.write(string)

+        if (kk<4):    

+            v_ = cumm_CFO[:,4*kk:4*kk+4]

+            print "{:10.4f} {:10.4f} {:10.4f} {:10.4f}".format(float(v_[:,0]),float(v_[:,1]),float(v_[:,2]),float(v_[:,3]))

+            string = "{:10.4f} {:10.4f} {:10.4f} {:10.4f}".format(float(v_[:,0]),float(v_[:,1]),float(v_[:,2]),float(v_[:,3]))

+            string = string + '\n'

+            fw.write(string)

+        else:

+            v_ = cumm_CFO[:,4*kk:]

+            print "{:10.4f} {:10.4f} {:10.4f}".format(float(v_[:,0]),float(v_[:,1]),float(v_[:,2]))

+            string = "{:10.4f} {:10.4f} {:10.4f}".format(float(v_[:,0]),float(v_[:,1]),float(v_[:,2]))

+            string = string + '\n'

+            fw.write(string)

+    print ""

+    fw.write('\n')

+

+def xor(a,b):

+    xor_result=[0]*len(a)

+    for i in range(len(a)):

+        temp = (int(a[i])^int(b[i]))

+        xor_result[i] = temp

+    return xor_result

+

+

+def diff_stable(first, second):

+    second = set(second)

+    return [item for item in first if item not in second]
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/IQ_analyzer_GUI.py b/mcu/tools/IQ_Analyzer/src/IQ_analyzer_GUI.py
new file mode 100644
index 0000000..0741b44
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/IQ_analyzer_GUI.py
@@ -0,0 +1,416 @@
+import Tkinter as tk

+import tkFileDialog

+import tkMessageBox

+import ttk 

+import os

+import sys

+

+from NR_IQ_Config import IQ_Input

+from NR_IQ_Analyzer import NR_IQ_Analyzer

+from LTE_IQ_Analyzer import LTE_IQ_Analyzer

+

+

+class MainWindow:

+    def __init__(self, master):

+        self.master = master

+        self.initUI()

+            

+    def initUI(self):

+        self.master.title("IQ Analyzer")

+        

+        tabControl = ttk.Notebook(self.master)        # Create Tab Control

+        self.lte_tab = ttk.Frame(tabControl)        # Create lte tab

+        tabControl.add(self.lte_tab, text = '4G/LTE')    # Add the tab

+        self.nr_tab = ttk.Frame(tabControl)        # Create nr tab

+        tabControl.add(self.nr_tab, text = '5G/NR')        

+        tabControl.pack(expand=1, fill='both')            # Pack to make visible

+        

+        # NR

+        self.nr_params = NR_InputBoxes()

+        nr = NR(self.nr_tab, self.nr_params)

+        # LTE

+        self.lte_params = LTE_tab_signal_params()

+        lte = LTE(self.lte_tab, self.lte_params)

+

+class NR(tk.Frame): 

+            

+    def __init__(self, master, nr_params):

+        self.master = master

+        tk.Frame.__init__(self, self.master)

+        

+        self.nr_params = nr_params

+        

+        # Frame "IQ data"

+        frame_d = tk.LabelFrame(self.master, text = 'IQ input', padx=5, pady=5)

+        frame_d.pack(side = "left", padx = 20, pady = 20)

+               

+        # Frame "Signal Description"

+        frame_a = tk.LabelFrame(self.master, text="Signal Description", padx=5, pady=5)

+        frame_a.pack(side = "left", expand = True, fill = "both", padx = 20, pady = 20)        

+        

+        # Frame "Plotting"

+        frame_b = tk.LabelFrame(self.master, text = "Plotting / Analysis", padx=5, pady=5)

+        frame_b.pack(side = "top", padx = 20, pady = 20)

+

+        ###################################################################

+        # --------------------- SIGNAL DESCRIPTION (B) ------------------ #

+        ###################################################################

+        # Labels for input definitions in signal config

+        lab1 = tk.Label(frame_a, text = "Frequency range:", anchor = 'nw')

+        lab3 = tk.Label(frame_a, text = "Subcarrier spacing (kHz):", anchor = 'w')

+        lab4 = tk.Label(frame_a, text = "Cell ID:", anchor = 'w')      

+        lab5 = tk.Label(frame_d, text = "Subframes to calculate:", anchor = 'w')    

+        lab6 = tk.Label(frame_d, text = "IQ data file:", anchor = 'w')

+    

+        row_index = 0

+        

+        # IQ sample file

+        nr_params.iq_data_file_box = ttk.Entry(frame_d)

+        nr_params.iq_data_file_box.pack()

+        nr_params.iq_data_file_box.grid(row=row_index, column=1)

+        lab6.pack()

+        lab6.grid(row=row_index, column=0, sticky = 'w')

+        chooseFileButton = ttk.Button(frame_d, text = "...", command = lambda: self.ChooseFile(nr_params.iq_data_file_box))

+        chooseFileButton.pack()

+        chooseFileButton.grid(row=row_index, column=2, padx=2, pady=5)

+        

+        # Checkbuttons

+        row_index = row_index + 1

+        checkButton = ttk.Checkbutton(frame_d, text = 'Swap I and Q', variable = nr_params.swap_iq)

+        checkButton.pack()

+        checkButton.grid(row = row_index, column = 1, columnspan = 2, sticky = "w", padx=2, pady=5)

+        row_index = row_index + 1

+                   

+        # Total number of frames to calculate

+        lab5.pack()

+        lab5.grid(row=row_index, column=0, sticky = 'w')

+        nr_params.frame_amount_box = ttk.Entry(frame_d)

+        nr_params.frame_amount_box.insert(0, '40')

+        nr_params.frame_amount_box.pack()

+        nr_params.frame_amount_box.grid(row=row_index, column=1, padx=2, pady=5)

+        row_index = row_index + 1

+ 

+        # Menu option for frequency range and corresponding bandwidth and subcarrier spacing selection

+        self.lmax_options = {'sub 6-GHz': [4,8],

+          'mmWave': [64]}

+        

+        self.scs_options = {'sub 6-GHz' : [15, 30],

+              'mmWave' : [120, 240]}

+			  

+        self.variable_a = nr_params.freq

+        self.variable_b = nr_params.lmax

+        self.variable_c = nr_params.scs

+            

+        self.optionmenu_a = tk.OptionMenu(frame_a, nr_params.freq, *self.lmax_options.keys())

+        self.optionmenu_b = tk.OptionMenu(frame_a, self.variable_b, '')

+        self.optionmenu_c = tk.OptionMenu(frame_a, self.variable_c, '')

+        self.variable_a.trace('w', self.Update_options)        # Track user frequency range selection

+    

+        # Specify Component Carrier Cell-ID

+        lab4.pack()

+        lab4.grid(row=row_index, column=0, sticky = 'w')

+        nr_params.cell_id_box = ttk.Entry(frame_a)

+        nr_params.cell_id_box.insert(0, '0')

+        nr_params.cell_id_box.pack()

+        nr_params.cell_id_box.grid(row=row_index, column=1, padx=2, pady=5)

+        

+        row_index = row_index + 1

+        # Optionmenu Frequency range

+        lab1.pack()

+        lab1.grid(row=row_index, column=0, sticky = 'w')

+        self.optionmenu_a.pack()

+        self.optionmenu_a.grid(row=row_index, column=1, padx=2, pady=5)

+        

+        row_index = row_index + 1

+        # Optionmenu Subcarrier spacing

+        lab3.pack()

+        lab3.grid(row=row_index, column=0, sticky = 'w')

+        self.optionmenu_c.pack()

+        self.optionmenu_c.grid(row=row_index, column=1, padx=2, pady=5)    

+                    

+        row_index = row_index + 1

+        

+        ###################################################################

+        # --------------------- SIGNAL DESCRIPTION (E) ------------------ #

+        ###################################################################

+        

+        ###################################################################

+        # --------------------- PLOTTING OPTIONS (B) -------------------- #

+        ###################################################################

+    

+        checkButtonNames = ['Frequency spectrum', 'Time graph', 'Cell verification', 'Resource grid', 'PBCH']

+        nr_params.plotting_options = []

+        plot_row_index = 0

+        for i in xrange(len(checkButtonNames)):

+            nr_params.plotting_options.append(tk.IntVar())

+            plotCheckButton = ttk.Checkbutton(frame_b, text = checkButtonNames[i], variable = nr_params.plotting_options[i])

+            plotCheckButton.pack()

+            plotCheckButton.grid(row = plot_row_index, column = 5, sticky = "WE")

+            plot_row_index = plot_row_index + 2

+             

+        ###################################################################

+        # --------------------- PLOTTING OPTIONS (B) -------------------- #

+        ###################################################################    

+        

+        # Run button

+        runButton = ttk.Button(self.master, text = "Run", command = self.NR_RunSelectedFiles)

+        runButton.pack(padx = 20, pady = 20)

+        

+        self.pack()

+    

+    def Update_options(self, *args):

+        

+        scs = self.scs_options[self.variable_a.get()]

+        self.variable_c.set(scs[0])

+        

+        menu_c = self.optionmenu_c['menu']

+        menu_c.delete(0, 'end')

+        

+        for j in scs:

+            menu_c.add_command(label=j, command=lambda s=j: self.variable_c.set(s))          

+            

+    def ChooseFile(self, file_box):

+        curr_dir_path = os.path.dirname(os.path.abspath(sys.argv[0]))

+        CONFIG_FILE = curr_dir_path + "\\IQ_analyzer_GUI.conf"

+        try:

+            with open(CONFIG_FILE,"r") as file:

+                file_path = file.read()

+                file.close()

+        except IOError:

+                file_path = curr_dir_path

+    

+        file_box.delete(0, "end")

+        file_box.insert(0, tkFileDialog.askopenfilename(initialdir=file_path, filetypes=[('Text files','*.txt')]))

+    

+        directory = os.path.split(file_box.get())[0]

+        if ( directory != '' ):

+            file = open(CONFIG_FILE,"w+")

+            file.write(directory)

+            file.close()

+        

+    def NR_RunSelectedFiles(self):

+

+        iq_input = IQ_Input()

+

+        iq_input.NidCell = int(self.nr_params.cell_id_box.get())

+        iq_input.freq_range = self.nr_params.freq.get()

+        iq_input.scs = int(self.nr_params.scs.get())

+        iq_input.tot_num_of_frames = int(self.nr_params.frame_amount_box.get())

+        iq_input.swap_iq = self.nr_params.swap_iq.get()

+        iq_input.input_file_main = self.nr_params.iq_data_file_box.get()

+        iq_input.normBitWidth = self.nr_params.normBitWitdth.get()

+        iq_input.dataFormat = self.nr_params.dataFormat.get()

+        

+        iq_input.plotting_option = []    

+        for i in xrange(5):

+            iq_input.plotting_option.append(self.nr_params.plotting_options[i].get())        

+                

+        if iq_input.NidCell == '':

+            tkMessageBox.showinfo(message='Specify cell ID')

+        elif iq_input.tot_num_of_frames == '':

+            tkMessageBox.showinfo(message='Specify number of frames')

+        elif iq_input.freq_range == '':

+            tkMessageBox.showinfo(message='Specify frequency')

+        elif (iq_input.input_file_main == ''):

+            tkMessageBox.showinfo(message='Choose a file first')        

+        elif (iq_input.plotting_option == [0, 0, 0, 0, 0]):

+            tkMessageBox.showinfo(message='Choose plotting options')

+        else:

+            r = NR_IQ_Analyzer()

+            r.run(iq_input)

+

+

+class LTE:

+    def __init__(self, master, lte_params):

+        self.master = master

+    

+        bandwidth_list = [1.4, 3, 5, 10, 15, 20]

+        row_index = 0

+        self.lte_params = lte_params

+        

+        # Specify Component Carrier Cell-ID

+        lte_params.cell_id_box = ttk.Entry(self.master)

+        lte_params.cell_id_box.insert(0, '0')

+        lte_params.cell_id_box.grid(row = row_index, column = 1, padx = 2)

+        ttk.Label(self.master, text = 'Cell ID:').grid(row = row_index, column = 0, sticky = "e", padx = 2)

+

+        # Specify Duplexing Mode

+        lte_params.duplexing_mode.set(0)

+        ttk.Label(self.master, text = 'Duplexing mode:').grid(row = row_index, column = 4, sticky = "w", padx = 2)

+        ttk.Radiobutton(self.master, text = 'FDD', variable = lte_params.duplexing_mode, value = 0).grid(column = 4, row = row_index + 1, sticky = "w")

+        ttk.Radiobutton(self.master, text = 'TDD', variable = lte_params.duplexing_mode, value = 1).grid(column = 4, row = row_index + 2, sticky = "w")

+

+        # CP option

+        lte_params.cp_option.set(1)

+        ttk.Label(self.master, text = 'CP option:').grid(row = row_index, column = 5, sticky = "w", padx = 2)

+        ttk.Radiobutton(self.master, text = 'normal', variable = lte_params.cp_option, value = 1).grid(column = 5, row = row_index + 1, sticky = "w")

+        ttk.Radiobutton(self.master, text = 'extended', variable = lte_params.cp_option, value = 0).grid(column = 5, row = row_index + 2, sticky = "w")

+        row_index = row_index + 1

+

+        # Total number of frames to calculate

+        lte_params.frame_amount_box = ttk.Entry(self.master)

+        lte_params.frame_amount_box.insert(0, '40')

+        lte_params.frame_amount_box.grid(row = row_index, column = 1, padx = 2)

+        ttk.Label(self.master, text = 'Subframes to calculate:').grid(row = row_index, column = 0, sticky = "e", padx = 2)

+        row_index = row_index + 1

+

+        # FRC input

+        lte_params.frc_input_box = ttk.Entry(self.master)

+        lte_params.frc_input_box.insert(0, '0')

+        lte_params.frc_input_box.grid(row = row_index, column = 1, padx = 2)

+        ttk.Label(self.master, text = 'FRC:').grid(row = row_index, column = 0, sticky = "e", padx = 2)

+        row_index = row_index + 1

+

+        # Bandwidth

+        bandwidth_menu = ttk.OptionMenu(self.master, lte_params.bandwidth, bandwidth_list[5], *bandwidth_list)

+        bandwidth_menu.grid(row = row_index, column = 1, padx = 2, sticky = "w")

+        ttk.Label(self.master, text = 'Bandwidth:').grid(row = row_index, column = 0, sticky = "e", padx = 2)

+        row_index = row_index + 1

+

+        # Extra options

+        extra_options_row = 0

+        extra_options_column = 2

+        lte_params.show_grid_all_the_time.set(1)

+        ttk.Checkbutton(self.master, text = 'Jump to the last calculated subframe', variable = lte_params.jump_frame).grid(row = extra_options_row, column = extra_options_column, columnspan = 2, sticky = "w")

+        extra_options_row = extra_options_row + 1

+        ttk.Checkbutton(self.master, text = 'Show data grid all the time', variable = lte_params.show_grid_all_the_time).grid(row = extra_options_row, column = extra_options_column, columnspan = 2, sticky = "w")

+        extra_options_row = extra_options_row + 1

+        ttk.Checkbutton(self.master, text = 'Swap I and Q', variable = lte_params.swap_iq).grid(row = extra_options_row, column = extra_options_column, columnspan = 2, sticky = "w")

+        extra_options_row = extra_options_row + 1

+

+        # Plotting options

+        row_index = row_index + extra_options_row

+        checkbutton_names = ['Frequency domain', 'Time domain', 'PSS correlation', 'LTE resource grid']

+        lte_params.plotting_options = []

+        ttk.Label(self.master, text = 'Plotting options:').grid(row = row_index, column = 0, sticky = "e", padx = 2)

+        for i in xrange(4):

+            lte_params.plotting_options.append(tk.IntVar())

+            lte_params.plotting_options[i].set(1)

+            ttk.Checkbutton(self.master, text = checkbutton_names[i], variable = lte_params.plotting_options[i]).grid(row = row_index, column = i + 1)

+        row_index = row_index + 1

+

+        # IQ sample file

+        lte_params.iq_data_file_box = ttk.Entry(self.master, width = 40)

+        lte_params.iq_data_file_box.grid(row = row_index, column = 1, padx = 2, columnspan = 2)

+        ttk.Label(self.master, text = 'IQ data file:').grid(row = row_index, column =0, sticky = "e", padx = 2)

+        chooseIQFileButton = ttk.Button(self.master, text = "...", command = lambda: self.ChooseFile(lte_params.iq_data_file_box))

+        chooseIQFileButton.grid(row = row_index, column = 3, padx = 2, sticky = "w")

+        row_index = row_index + 1

+

+        # Time info file

+        lte_params.time_info_file_box = ttk.Entry(self.master, width = 40)

+        lte_params.time_info_file_box.grid(row = row_index, column = 1, padx = 2, columnspan = 2)

+        ttk.Label(self.master, text = 'Time info file:').grid(row = row_index, column =0, sticky = "e", padx = 2)

+        chooseTimeFileButton = ttk.Button(self.master, text = "...", command = lambda: self.ChooseFile(lte_params.time_info_file_box))

+        chooseTimeFileButton.grid(row = row_index, column = 3, padx = 2, sticky = "w")

+

+        # Buttons

+        runButton = ttk.Button(self.master, text = "Run", command = self.LTE_RunSelectedFiles)

+        runButton.grid(row = row_index, column = 5, padx = 2)

+        row_index = row_index + 1

+        

+    

+    def ChooseFile(self, file_box):

+        curr_dir_path = os.path.dirname(os.path.abspath(sys.argv[0]))

+        CONFIG_FILE = curr_dir_path + "\\IQ_analyzer_GUI.conf"

+        try:

+            with open(CONFIG_FILE,"r") as file:

+                file_path = file.read()

+                file.close()

+        except IOError:

+                file_path = curr_dir_path

+    

+        file_box.delete(0, "end")

+        file_box.insert(0, tkFileDialog.askopenfilename(initialdir=file_path, filetypes=[('Text files','*.txt')]))

+    

+        directory = os.path.split(file_box.get())[0]

+        if ( directory != ''):

+            file = open(CONFIG_FILE,"w+")

+            file.write(directory)

+            file.close()

+        

+    def LTE_RunSelectedFiles(self):

+        iq_input = IQ_Input()

+

+        try:

+            iq_input.chose_rate = self.lte_params.bandwidth.get()

+            iq_input.is_TDD = self.lte_params.duplexing_mode.get()

+            iq_input.NidCell = int(self.lte_params.cell_id_box.get())

+            iq_input.Ncp_type = self.lte_params.cp_option.get()

+            

+            if (( self.lte_params.plotting_options[0].get() == 1 ) and ( self.lte_params.plotting_options[1].get() == 1 ) and ( self.lte_params.plotting_options[2].get() == 1 ) and ( self.lte_params.plotting_options[3].get() == 1 )):

+                iq_input.plotting_option = 1

+            elif (( self.lte_params.plotting_options[1].get() == 1 ) and ( self.lte_params.plotting_options[2].get() == 1 ) and ( self.lte_params.plotting_options[3].get() == 1 )):

+                iq_input.plotting_option = 2

+            elif (( self.lte_params.plotting_options[2].get() == 1 ) and ( self.lte_params.plotting_options[3].get() == 1 )):

+                iq_input.plotting_option = 3

+

+            iq_input.FRC_input = int(self.lte_params.frc_input_box.get())

+            iq_input.tot_num_of_frames = int(self.lte_params.frame_amount_box.get())

+            iq_input.is_plotTimeGraphs = 1 # NOT NEEDED?

+            iq_input.is_plotFreqGraphs = 0 # NOT NEEDED?

+            iq_input.num_avg_frames = 10 # TODO

+            iq_input.is_averagedFrames = 1 # TODO

+            iq_input.is_avgSlidingWindow = 1 # TODO

+            iq_input.swap_iq = self.lte_params.swap_iq.get()

+            iq_input.input_file_main = self.lte_params.iq_data_file_box.get()

+            iq_input.input_file_div  = self.lte_params.iq_data_file_box.get()

+            iq_input.input_time_file = self.lte_params.time_info_file_box.get()

+

+            if iq_input.NidCell == '':

+                tkMessageBox.showinfo(message='Specify cell ID')

+            elif iq_input.tot_num_of_frames == '':

+                tkMessageBox.showinfo(message='Specify number of frames')

+            elif (iq_input.input_file_main == ''):

+                tkMessageBox.showinfo(message='Choose a file first')               

+                

+            else:

+                iq_input.is_NR = 0

+                r = LTE_IQ_Analyzer()

+                r.run(iq_input)

+

+        except ValueError:

+            tkMessageBox.showinfo(message='Use only integers')        

+            

+class LTE_tab_signal_params: 

+    

+    def __init__(self):

+        self.cell_id_box = 0

+        self.duplexing_mode = tk.IntVar()

+        self.cp_option = tk.IntVar()

+        self.frame_amount_box = 0

+        self.frc_input_box = 0

+        self.bandwidth = tk.IntVar()

+        self.jump_frame = tk.IntVar()

+        self.show_grid_all_the_time = tk.IntVar()

+        self.swap_iq = tk.BooleanVar()

+        self.plotting_options = 0

+        self.iq_data_file_box = 0

+        self.time_info_file_box = 0

+        

+

+class NR_InputBoxes:

+    def __init__(self):

+        self.cell_id_box = 0

+        self.cp_option = tk.StringVar()

+        self.frame_amount_box = 0

+        self.freq = tk.StringVar()

+        self.lmax = tk.StringVar()

+        self.scs = tk.StringVar()

+        self.swap_iq = tk.BooleanVar()

+        self.plotting_options = 0

+        self.iq_data_file_box = 0

+        self.sampling_rate = tk.StringVar()

+        self.normBitWitdth = tk.BooleanVar()

+        self.dataFormat = tk.StringVar()

+               

+def main(): 

+    root  = tk.Tk()

+    main_window = MainWindow(root)

+    root.mainloop()

+

+

+if __name__ == '__main__':

+    main()

+    

+    
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/LTE_IQ_Analyzer.py b/mcu/tools/IQ_Analyzer/src/LTE_IQ_Analyzer.py
new file mode 100644
index 0000000..dfe7f0b
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/LTE_IQ_Analyzer.py
@@ -0,0 +1,968 @@
+#*****************************************************************************

+#  Copyright Statement:

+#  --------------------

+#  This software is protected by Copyright and the information contained

+#  herein is confidential. The software may not be copied and the information

+#  contained herein may not be used or disclosed except with the written

+#  permission of MediaTek Inc. (C) 2005

+#

+#  BY OPENING THIS FILE, BUYER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES

+#  THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")

+#  RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO BUYER ON

+#  AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,

+#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF

+#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.

+#  NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE

+#  SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR

+#  SUPPLIED WITH THE MEDIATEK SOFTWARE, AND BUYER AGREES TO LOOK ONLY TO SUCH

+#  THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. MEDIATEK SHALL ALSO

+#  NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE RELEASES MADE TO BUYER'S

+#  SPECIFICATION OR TO CONFORM TO A PARTICULAR STANDARD OR OPEN FORUM.

+#

+#  BUYER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND CUMULATIVE

+#  LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,

+#  AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,

+#  OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY BUYER TO

+#  MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.

+#

+#  THE TRANSACTION CONTEMPLATED HEREUNDER SHALL BE CONSTRUED IN ACCORDANCE

+#  WITH THE LAWS OF THE STATE OF CALIFORNIA, USA, EXCLUDING ITS CONFLICT OF

+#  LAWS PRINCIPLES.  ANY DISPUTES, CONTROVERSIES OR CLAIMS ARISING THEREOF AND

+#  RELATED THERETO SHALL BE SETTLED BY ARBITRATION IN SAN FRANCISCO, CA, UNDER

+#  THE RULES OF THE INTERNATIONAL CHAMBER OF COMMERCE (ICC).

+#

+#****************************************************************************/

+

+#*****************************************************************************

+#

+#* Filename:

+#* ---------

+#*   DFE_IQ_Analyzer_dev.py

+#*

+#* Description:

+#* ------------

+#*   RF-DFE IQ Analyzer Tool

+#*

+#* Author:

+#* -------

+#*   Gerardo Moreno (mtk10872)

+#*

+

+

+import math

+import os 

+import sys

+

+import numpy as np

+from scipy import signal

+import IQ_Utils

+from IQ_Params import IqParams

+from IQ_Signals import IqSignals

+from IQ_Plots import FreqDomainPlots

+from IQ_Plots import TimeDomainPlots

+from IQ_Plots import PssCorrPlots

+from IQ_Plots import ResourceGrid

+from IQ_Plots import PilotsPlots

+from IQ_Plots import PilotsPhasePlots

+from IQ_Plots import CompensatedDataPlots

+from IQ_Decoder import SS_Decoder

+from IQ_Utils import L

+

+import matplotlib.pyplot as plt

+import matplotlib.gridspec as gridspec

+

+import warnings

+warnings.filterwarnings('ignore')

+

+

+np.set_printoptions(threshold='nan')

+

+class IQ_Input:

+		

+	###################################################################

+	# ------------------ USER-DEFINED PARAMATERS (B) -----------------#

+	###################################################################

+	

+	# is_NR = 0 (LTE)

+	# is_NR = 1 (NR)

+	is_NR= 0

+	

+	chose_rate = 20

+	

+	# Specify Duplexing Mode

+	#		is_TDD = 1	(TDD)

+	#		is_TDD = 0	(FDD)

+	is_TDD = 1

+	

+	#	Specify Component Carrier Cell-ID

+	#NidCell = 412

+	#NidCell = 413

+	NidCell = 1

+	

+	# CP option: 

+	#		Ncp_type = 1	(normal)

+	#		Ncp_type = 0	(extended)

+	Ncp_type = 1

+	

+	#

+	# 1

+	# 2

+	# 3

+	#

+	plotting_option = 3

+	

+	swap_iq = False

+	

+	#FRC_input = 64308500

+	#FRC_input = 64226000

+	#FRC_input = 3674023

+	FRC_input = 0

+	

+	tot_num_of_frames = 20

+		

+	num_avg_frames = 10

+	is_averagedFrames = 1

+	is_avgSlidingWindow = 1

+	

+	# ------------------ RAW I-Q INPUT SAMPLES FILE (B) ------------------

+	#input_file_main = "\\3CC_BLER_IQ_CAL\\LTE_INTP_DEMOD_P0P11_C1A1.txt"

+	#input_file_div = "\\3CC_BLER_IQ_CAL\\LTE_INTP_DEMOD_P0P11_C1A1.txt"

+	#input_time_file = "\\3CC_BLER_IQ_CAL\\LTE_INTP_DEMOD_P0P11_C1A1_TIME.txt"

+	

+	#input_file_main = "\\3CC_BLER_IQ_CAL\\big_bler\\LTE_INTP_DEMOD_P0P11_C1A1.txt"

+	#input_file_div = "\\3CC_BLER_IQ_CAL\\big_bler\\LTE_INTP_DEMOD_P0P11_C1A1.txt"

+	#input_time_file = "\\3CC_BLER_IQ_CAL\\big_bler\\LTE_INTP_DEMOD_P0P11_C1A1_TIME.txt"

+	#input_file_main = "\\3CC_BLER_IQ_CAL\\big_bler\\LTE_INTP_DEMOD_P0P21_C1A0.txt"

+	#input_file_div = "\\3CC_BLER_IQ_CAL\\big_bler\\LTE_INTP_DEMOD_P0P21_C1A0.txt"

+	#input_time_file = "\\3CC_BLER_IQ_CAL\\big_bler\\LTE_INTP_DEMOD_P0P21_C1A0_TIME.txt"

+	

+	#input_file_main = "\\3CC_NO_BLER\\LTE_INTP_DEMOD_P0P11_C1A1.txt"

+	#input_file_div = "\\3CC_NO_BLER\\LTE_INTP_DEMOD_P0P11_C1A1.txt"

+	#input_time_file = "\\3CC_NO_BLER\\LTE_INTP_DEMOD_P0P11_C1A1_TIME.txt"

+	#input_file_main = "\\3CC_NO_BLER\\LTE_INTP_DEMOD_P0P21_C1A0.txt"

+	#input_file_div = "\\3CC_NO_BLER\\LTE_INTP_DEMOD_P0P21_C1A0.txt"

+	#input_time_file = "\\3CC_NO_BLER\\LTE_INTP_DEMOD_P0P21_C1A0_TIME.txt"

+	

+	#input_file_main = "\\SignalAnalysis_Verification\\LTE_INTP_DEMOD_P0P12_C0A0_20MHz_TDD.txt"

+	#input_file_div = "\\SignalAnalysis_Verification\\LTE_INTP_DEMOD_P0P12_C0A0_20MHz_TDD.txt"

+	#input_time_file = "\\SignalAnalysis_Verification\\LTE_INTP_DEMOD_P0P12_C0A0_TIME_20MHz_TDD.txt"

+	

+	# 3CC Anritsu - TDD PCell Main

+	#input_file_main = "\\3CC_Anritsu\\TDD_B39B41CCA\\LTE_INTP_DEMOD_P0P11_C1A1.txt"

+	#input_file_div = "\\3CC_Anritsu\\TDD_B39B41CCA\\LTE_INTP_DEMOD_P0P11_C1A1.txt"

+	#input_time_file = "\\3CC_Anritsu\\TDD_B39B41CCA\\LTE_INTP_DEMOD_P0P11_C1A1_TIME.txt"

+	# 3CC Anritsu - TDD PCell Diversity

+	#input_file_main = "\\3CC_Anritsu\\TDD\\LTE_INTP_DEMOD_P0P12_C0A0.txt"

+	#input_file_div = "\\3CC_Anritsu\\TDD\\LTE_INTP_DEMOD_P0P12_C0A0.txt"

+	#input_time_file = "\\3CC_Anritsu\\TDD\\LTE_INTP_DEMOD_P0P12_C0A0_TIME.txt"

+	

+	# 3CC Anritsu - FDD PCell Diversity

+	input_file_main = "\\3CC_Anritsu\\FDD_B3B7CCA\\LTE_INTP_DEMOD_P0P12_C0A0.txt"

+	input_file_div  = "\\3CC_Anritsu\\FDD_B3B7CCA\\LTE_INTP_DEMOD_P0P12_C0A0.txt"

+	input_time_file = "\\3CC_Anritsu\\FDD_B3B7CCA\\LTE_INTP_DEMOD_P0P12_C0A0_TIME.txt"

+	# 3CC Anritsu - FDD PCell Main

+	#input_file_main = "\\3CC_Anritsu\\FDD_B3B7CCA\\LTE_INTP_DEMOD_P0P11_C1A1.txt"

+	#input_file_div = "\\3CC_Anritsu\\FDD_B3B7CCA\\LTE_INTP_DEMOD_P0P11_C1A1.txt"

+	#input_time_file = "\\3CC_Anritsu\\FDD_B3B7CCA\\LTE_INTP_DEMOD_P0P11_C1A1_TIME.txt"

+	

+	#input_file_main = "\\TCL_DISABLED\\LTE_INTP_DEMOD_P0P21_C1A0.txt"

+	#input_file_div = "\\TCL_DISABLED\\LTE_INTP_DEMOD_P0P21_C1A0.txt"

+	#input_time_file = "\\TCL_DISABLED\\LTE_INTP_DEMOD_P0P21_C1A0_TIME.txt"

+	

+	# ------------------ RAW I-Q INPUT SAMPLES FILE (E) ------------------

+

+

+

+###################################################################

+# ------------------ USER-DEFINED PARAMATERS (E) -----------------#

+###################################################################

+			

+class LTE_IQ_Analyzer:

+	def __init__(self):

+		print "Ready"

+		

+	def run(self, args):

+		is_NR = args.is_NR

+		chose_rate = args.chose_rate

+		is_TDD  = args.is_TDD

+		NidCell  = args.NidCell

+		Ncp_type  = args.Ncp_type

+		plotting_option  = args.plotting_option

+		FRC_input  = args.FRC_input

+		tot_num_of_frames  = args.tot_num_of_frames

+				

+		num_avg_frames  = args.num_avg_frames

+		is_averagedFrames  = args.is_averagedFrames

+		is_avgSlidingWindow  = args.is_avgSlidingWindow

+		input_file_main  = args.input_file_main

+		input_time_file  = args.input_time_file

+		swap_iq = args.swap_iq	

+		

+		

+		

+		############################################################

+		# ------------------ GLOBAL CONSTANTS (B) -----------------#

+		############################################################

+		

+		curr_dir_path = os.path.dirname(os.path.abspath(sys.argv[0]))

+		

+		file_path_main = input_file_main

+		time_file_path = input_time_file

+		

+		fr_MAIN = open(file_path_main,"r+")

+#		if ( time_file_path != '' ):

+#			fr_TIME = open(time_file_path,"r+")

+#		else: # time file not defined

+#			FRC_input = 0

+#			

+		if (time_file_path == ''):

+			FRC_input = 0

+		

+		f_path = curr_dir_path + "\\prompt_traces.txt"

+		L.logopen(f_path)

+		

+		############################################################

+		# ------------------ GLOBAL CONSTANTS (E) -----------------#

+		############################################################

+		params = IqParams(chose_rate, Ncp_type)

+		

+		params.is_averagedFrames = is_averagedFrames

+		params.is_avgSlidingWindow = is_avgSlidingWindow

+		params.num_avg_frames = num_avg_frames

+		params.tot_num_of_frames = tot_num_of_frames

+		

+		params.is_TDD = is_TDD

+		params.is_NR = is_NR

+		

+		signals = IqSignals(params, NidCell, is_TDD) 

+	

+		ss_decoder = SS_Decoder(params, signals)

+		####################################################################

+		# ------------------ LTE-SPECIFIC PARAMATERS (E) ------------------#

+		####################################################################

+				

+		

+		####################################################################

+		# ------------------ PLOTTING INIT OPTION-(1) (B) -----------------#

+		####################################################################

+		if (plotting_option == 1):

+		

+			fig = plt.figure(facecolor='black')

+			gs = gridspec.GridSpec(3, 2)	

+								

+			## ------------------ FREQUENCY DOMAIN PLOTS INIT (B) ------------------

+			ax_m = FreqDomainPlots(params, gs[0,0])

+			## ------------------ FREQUENCY DOMAIN PLOTS INIT (E) ------------------

+			

+			## ------------------ TIME DOMAIN PLOTS INIT (B) ------------------

+			ax_t_i_main = TimeDomainPlots(params, gs[1,0])

+			## ------------------ TIME DOMAIN PLOTS INIT (E) -----------------

+			

+			## ------------------ PSS CORRELATION PLOTS INIT (B) ------------------

+			ax_pss_main = PssCorrPlots(params, gs[2,0])

+			## ------------------ PSS CORRELATION PLOTS INIT (E) ------------------

+			

+			## ------------------ LTE RESOURCE GRID PLOT INIT (B) ------------------

+			ax_5msFD_grid_main = ResourceGrid(params, gs[:,1])

+			## ------------------ LTE RESOURCE GRID PLOT INIT (E) ------------------

+				

+			plt.tight_layout()

+		####################################################################

+		# ------------------ PLOTTING INIT OPTION-(1) (E) -----------------#

+		####################################################################

+		

+		

+		

+		####################################################################

+		# ------------------ PLOTTING INIT OPTION-(2) (B) -----------------#

+		####################################################################

+		if (plotting_option == 2):

+		

+			fig = plt.figure(facecolor='black')

+			gs = gridspec.GridSpec(4, 5)					

+			## ------------------ TIME DOMAIN PLOTS INIT (B) ------------------

+			ax_t_i_main =	TimeDomainPlots(params, gs[0,:2])

+			## ------------------ TIME DOMAIN PLOTS INIT (E) -----------------

+		

+			## ------------------ PSS CORRELATION PLOTS INIT (B) ------------------

+			ax_pss_main = PssCorrPlots(params, gs[1,:2])		

+			## ------------------ PSS CORRELATION PLOTS INIT (E) ------------------

+		

+			## ------------------ LTE RESOURCE GRID PLOT INIT (B) ------------------			

+			ax_5msFD_grid_main = ResourceGrid(params,gs[:2,2:])

+			## ------------------ LTE RESOURCE GRID PLOT INIT (E) ------------------

+		 

+		 

+			## ------------------ PILOTS PLOTS INIT (B) ------------------

+			

+			# Subframe 0 ************************************************

+			

+			ax_pilots_sf0_main = PilotsPlots(params, gs[2,0], 0)			

+			ax_pilots_phase_sf0_main = PilotsPhasePlots(params,gs[3,0],0)

+			# Subframe 1 ************************************************

+			ax_pilots_sf1_main =  PilotsPlots(params,gs[2,1],1)			

+			ax_pilots_phase_sf1_main = PilotsPhasePlots(params,gs[3,1],1)

+			# Subframe 2 ************************************************

+			ax_pilots_sf2_main = PilotsPlots(params,gs[2,2],2)

+			ax_pilots_phase_sf2_main = PilotsPhasePlots(params,gs[3,2],2)

+			# Subframe 3 ************************************************

+			ax_pilots_sf3_main = PilotsPlots(params,gs[2,3],3)

+			ax_pilots_phase_sf3_main = PilotsPhasePlots(params,gs[3,3],3)

+			# Subframe 4 ************************************************

+			ax_pilots_sf4_main = PilotsPlots(params,gs[2,4],4)

+			ax_pilots_phase_sf4_main = PilotsPhasePlots(params,gs[3,4],4)

+			## ------------------ PILOTS PLOTS INIT (E) ------------------

+			

+			plt.tight_layout()

+		####################################################################

+		# ------------------ PLOTTING INIT OPTION-(2) (E) -----------------#

+		####################################################################

+		

+		

+		####################################################################

+		# ------------------ PLOTTING INIT OPTION-(3) (B) -----------------#

+		####################################################################

+		if (plotting_option == 3):

+		

+			fig = plt.figure(facecolor='black')

+			gs = gridspec.GridSpec(4,6)	

+						

+			## ------------------ LTE RESOURCE GRID PLOT INIT (B) ------------------			

+			ax_5msFD_grid_main = ResourceGrid(params,gs[2:,:2])			

+			## ------------------ LTE RESOURCE GRID PLOT INIT (E) ------------------

+		 

+		 

+			## ------------------ COMPENSATED DATA PLOTS INIT (B) ------------------

+			

+			# Subframe 0 ************************************************

+			ax_data_sf0 = CompensatedDataPlots(params,gs[:2,:2],0)					

+			# Subframe 1 ************************************************

+			ax_data_sf1 = CompensatedDataPlots(params,gs[:2,2:4],1)

+			# Subframe 2 ************************************************

+			ax_data_sf2 = CompensatedDataPlots(params,gs[:2,4:],2)			

+			# Subframe 3 ************************************************

+			ax_data_sf3 = CompensatedDataPlots(params,gs[2:,2:4],3)			

+			# Subframe 4 ************************************************

+			ax_data_sf4 = CompensatedDataPlots(params,gs[2:,4:],4)			

+			## ------------------ PILOTS PLOTS INIT (E) ------------------

+			

+			plt.tight_layout()

+		####################################################################

+		# ------------------ PLOTTING INIT OPTION-(3) (E) -----------------#

+		####################################################################

+	

+		def CSRS_extractionFunc(occupiedRBs_mat):

+			

+			CSRS_mat = np.vectorize(complex)(np.zeros((2*params.numRB,4*5)))

+			CSRS_mat[:,::2] = occupiedRBs_mat[cell_ID_offset_0:12*params.numRB:6,::7]

+			CSRS_mat[:,1::2] = occupiedRBs_mat[cell_ID_offset_1:12*params.numRB:6,4::7]

+			

+			return CSRS_mat

+		

+		

+		def genPilotsMat(HsubF_offset):

+			

+			#Generate 

+			m_prime_start_idx = 0+signals.NmaxRB-params.numRB

+			m_prime_stop_idx = (2*params.numRB-1)+signals.NmaxRB-params.numRB

+			

+			genPilot_mat = np.vectorize(complex)(np.zeros((2*params.numRB,4*5)))

+			genPilot_mat[:,::2] = signals.CSRS_mat[m_prime_start_idx:m_prime_stop_idx+1,HsubF_offset:10*7+HsubF_offset:7]

+			if (HsubF_offset > 0 ): 

+				genPilot_mat[:,1::2] = signals.CSRS_mat[m_prime_start_idx:m_prime_stop_idx+1,HsubF_offset+4:20*7:7]

+			else:

+				genPilot_mat[:,1::2] = signals.CSRS_mat[m_prime_start_idx:m_prime_stop_idx+1,4:10*7:7]

+			

+			return genPilot_mat

+		

+		

+		def genPilotGainPhaseComp_mat(fetchPilos_mat,refPilots_mat):

+		

+			GainPhaseComp_mat = np.vectorize(complex)(np.zeros((12*params.numRB,14*5)))

+			

+			pilots_GainPhase_mat = np.multiply(fetchPilos_mat, np.conjugate(refPilots_mat))

+			

+			#Map onto gain-phase compensation matrix

+			GainPhaseComp_mat[cell_ID_offset_0:12*params.numRB:6,::7] = pilots_GainPhase_mat[:,::2]			#Map 0th colums

+			GainPhaseComp_mat[cell_ID_offset_1:12*params.numRB:6,4::7] = pilots_GainPhase_mat[:,1::2]		#Map 4th colums

+			

+			return GainPhaseComp_mat

+		

+		

+		def CFO_calc(comp_mat, cell_id):

+			

+			pilots_mat = CSRS_extractionFunc(comp_mat)

+				

+			# Construct virtual pilots along freq-axis

+			VP_freqAxis = 0.5 * (pilots_mat[1:,:]+pilots_mat[:-1,:])

+			

+			#Map virtual pilots along freq-axis

+			limit_idx1 = 12*(params.numRB-1)+6+cell_ID_offset_0

+			comp_mat[cell_ID_offset_0+3:limit_idx1+1:6,::7] = VP_freqAxis[:,::2]		#Map 0th colums

+			limit_idx2 = 12*(params.numRB-1)+6+cell_ID_offset_1	

+			comp_mat[cell_ID_offset_1+3:limit_idx2+1:6,4::7] = VP_freqAxis[:,1::2]		#Map 4th colums		

+			

+			offset_ = cell_ID_offset_0

+			if (cell_ID_offset_1 < cell_ID_offset_0):

+				offset_ = cell_ID_offset_1

+				

+			VP_grid = np.vectorize(complex)(np.zeros((4*params.numRB,4*5)))

+			VP_grid[:,::2] = comp_mat[offset_:12*params.numRB:3,::7]

+			VP_grid[:,1::2] = comp_mat[offset_:12*params.numRB:3,4::7]

+			

+			#Remove first and last pilot rows	for accurate CFO measurement		

+			VP_grid = VP_grid[1:-1,:]

+			

+			CFO_mat = np.multiply(VP_grid[:,1:],np.conjugate(VP_grid[:,:-1]))

+			CFO_large_ = np.reshape(np.sum(CFO_mat[:,::2],axis=0),(1,10))

+			CFO_small_ = np.reshape(np.sum(CFO_mat[:,1::2],axis=0),(1,9))

+			

+			combined_CFO = np.zeros((1,4*5-1))

+			combined_CFO[:,::2] = np.angle(CFO_large_) / (4*2*math.pi) * 14000

+			combined_CFO[:,1::2] = np.angle(CFO_small_) / (3*2*math.pi) * 14000

+				

+			return combined_CFO

+		

+		def genCompMat(input_mat, fir_taps):

+			

+			x_ = np.zeros((len(fir_taps),1))

+			x_[:,0] = fir_taps

+		

+			z = signal.convolve2d(input_mat,x_)

+			z = z[24:-24,:]

+			

+			compMat = np.vectorize(complex)(np.zeros((12*params.numRB,14*5)))

+			for kk in xrange(0,10):

+				

+				if (kk<9):

+					

+					slot_mat = z[:,7*kk:7*(kk+1)+1]	

+				

+					C0 = np.reshape(slot_mat[:,0],(12*params.numRB,1))

+					C1 = np.reshape(slot_mat[:,4],(12*params.numRB,1))

+					C2 = np.reshape(slot_mat[:,7],(12*params.numRB,1))

+					

+					diffmat1 = (C1-C0) * np.reshape([0,0.25,0.5,0.75],(1,4))

+					compMat[:,7*kk:7*kk+4] = C0 + diffmat1

+					diffmat2 = (C2-C1) * np.reshape([0,1/3.0,2/3.0],(1,3))

+					compMat[:,7*kk+4:7*kk+7] = C1 + diffmat2

+					

+				else:

+					

+					slot_mat = z[:,7*kk:7*(kk+1)+1]	

+				

+					C0 = np.reshape(slot_mat[:,0],(12*params.numRB,1))

+					C1 = np.reshape(slot_mat[:,4],(12*params.numRB,1))

+								

+					diffmat1 = (C1-C0) * np.reshape([0,0.25,0.5,0.75,1.0,1.25,1.50],(1,7))

+					compMat[:,7*kk:7*kk+7] = C0 + diffmat1

+					

+			return compMat

+		

+		# ------------------ FRAME PROCESSING LOOP CONSTANTS (B) ------------------

+		

+		

+		bitWidth_scale_factor = (1.0 / pow(2,11))

+		frame_counter = 0

+		

+		start_half_frame = 1

+		# For pilots processing

+		v_shift = NidCell%6 

+		cell_ID_offset_0 = (0+v_shift)%6		#(v_0+v_shift)%6 - 3GPP TS 3.211 v10.0.0 Section 6.10.1.2

+		cell_ID_offset_1 = (3+v_shift)%6		#(v_1+v_shift)%6 - 3GPP TS 3.211 v10.0.0 Section 6.10.1.2

+		

+		#For CFO stats

+		cfo_counter = 0

+		win_avg = 20

+		cumm_cfo_vec = np.zeros((win_avg,4*5-1))

+		

+		IQ_fullLTEframe_main = [0] * 10 * params.analysis_frame_len

+

+		FIR_taps = [-0.000000000000114, 		# FIR filter taps for interpolation

+		  -0.003189874515215,

+		  -0.005660513749604,

+		  -0.006777513830684,

+		  -0.006160920810316,

+		  -0.003780390933230,

+		  -0.000000000000455,

+		   0.017882242985706,

+		   0.032307798207512,

+		   0.039457774230414,

+		   0.036666881143901,

+		   0.023060753594564,

+		                   0,

+		  -0.059774001562801,

+		  -0.111969705410957,

+		  -0.142658093427599,

+		  -0.139403129279344,

+		  -0.093181351461681,

+		                   0,

+		   0.186880191407909,

+		   0.398338278166193,

+		   0.609836360659756,

+		   0.795634962721124,

+		   0.931957901255373,

+		   1.000000000000909,

+		   0.931957901255373,

+		   0.795634962721124,

+		   0.609836360659756,

+		   0.398338278166193,

+		   0.186880191407909,

+		                   0,

+		  -0.093181351461681,

+		  -0.139403129279344,

+		  -0.142658093427599,

+		  -0.111969705410957,

+		  -0.059774001562801,

+		                   0,

+		   0.023060753594564,

+		   0.036666881143901,

+		   0.039457774230414,

+		   0.032307798207512,

+		   0.017882242985706,

+		  -0.000000000000455,

+		  -0.003780390933230,

+		  -0.006160920810316,

+		  -0.006777513830684,

+		  -0.005660513749604,

+		  -0.003189874515215,

+		  -0.000000000000114]

+		

+		half_frame2D_FD_full = np.vectorize(complex)(np.zeros((params.Nfft, 14*5)))

+		half_frame2D_FD_occupiedRB = np.vectorize(complex)(np.zeros((12*params.numRB, 14*5)))

+		

+		# ------------------ FRAME PROCESSING LOOP CONSTANTS (E) ------------------

+

+		######################################################################################

+		# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

+		# ++++++++++++++++++++++++++++ FRAME PROCESSING LOOP (B) ++++++++++++++++++++++++++++#

+		# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

+		######################################################################################

+		

+		if (FRC_input == 0):

+			print "\n"

+			print "input FRC = 0: Starting IQ samples from beginning of log...."

+		else:	

+			TimeFileMatrix = IQ_Utils.readTimeInfoFile(time_file_path)

+			iq_count = IQ_Utils.det_iqPairCount(TimeFileMatrix, FRC_input)

+			print "\n"

+			print "Locating input FRC in raw IQ data...."

+			for ii in xrange(0,iq_count):

+				fr_MAIN.readline()

+		

+		print "\n"

+		print "\n"

+		print "Filling the frame buffer"

+		for frame_iter in xrange(0, 7):

+			

+			frame_counter = frame_counter + 1

+			

+			L.info("subframe count:" + str(frame_iter))

+			

+			I_frame_main = [0] * params.analysis_frame_len

+			Q_frame_main = [0] * params.analysis_frame_len

+			

+			line_counter = 0

+			

+			for run_select in xrange(0, params.analysis_frame_len):

+				

+				# ------------------ MAIN Antenna Frame Generation ------------------

+				line_main= fr_MAIN.readline()

+				if ((line_main.find(";") != 0) and (len(line_main) > 5)):

+					#line_main = line_main.replace(" ", "")	#remove spaces

+					line_main = line_main.replace("0x", "")	#remove 0x

+					line_main = line_main.rstrip('\n')			#remove end_of_line character

+		

+					line_main = line_main.split()

+					

+					I_frame_main[line_counter] = IQ_Utils.twos_comp(int(line_main[int(swap_iq)],16),12)

+					Q_frame_main[line_counter] = IQ_Utils.twos_comp(int(line_main[int(swap_iq)^1],16),12)

+

+					

+				line_counter = line_counter + 1

+			

+			##############################################################################

+			# ------------------ TIME-DOMAIN IQ SAMPLES PROCESSING (B) ------------------#

+			##############################################################################

+			

+			# Combine I and Q paths  

+			IQ_frame_main = np.vectorize(complex)(I_frame_main, Q_frame_main)

+			

+			# Normalize bitWidth

+			IQ_frame_main_norm = (bitWidth_scale_factor)*IQ_frame_main

+				

+			IQ_fullLTEframe_main[:9*params.analysis_frame_len] = IQ_fullLTEframe_main[params.analysis_frame_len:]

+			IQ_fullLTEframe_main[9*params.analysis_frame_len:]	= IQ_frame_main_norm	

+		print "Processing..."

+		for frame_iter in xrange(7, tot_num_of_frames+1):

+			

+			frame_counter = frame_counter + 1

+

+			L.info("subframe count:" + str(frame_iter))

+			

+			I_frame_main = [0] * params.analysis_frame_len

+			Q_frame_main = [0] * params.analysis_frame_len

+			

+			line_counter = 0

+			

+			for run_select in xrange(0, params.analysis_frame_len):

+				

+				# ------------------ MAIN Antenna Frame Generation ------------------

+				line_main= fr_MAIN.readline()

+				if ((line_main.find(";") != 0) and (len(line_main) > 5)):

+					#line_main = line_main.replace(" ", "")	#remove spaces

+					line_main = line_main.replace("0x", "")	#remove 0x

+					line_main = line_main.rstrip('\n')			#remove end_of_line character

+		

+					line_main = line_main.split()

+					I_frame_main[line_counter] = IQ_Utils.twos_comp(int(line_main[int(swap_iq)],16),12)

+					Q_frame_main[line_counter] = IQ_Utils.twos_comp(int(line_main[int(swap_iq)^1],16),12)

+					

+				line_counter = line_counter + 1

+			

+			##############################################################################

+			# ------------------ TIME-DOMAIN IQ SAMPLES PROCESSING (B) ------------------#

+			##############################################################################

+			

+			# Combine I and Q paths

+			IQ_frame_main = np.vectorize(complex)(I_frame_main, Q_frame_main)

+			

+			# Normalize bitWidth

+			IQ_frame_main_norm = (bitWidth_scale_factor)*IQ_frame_main

+				

+			IQ_fullLTEframe_main[:9*params.analysis_frame_len] = IQ_fullLTEframe_main[params.analysis_frame_len:]

+			IQ_fullLTEframe_main[9*params.analysis_frame_len:]	= IQ_frame_main_norm	

+				

+		

+			######################################################################################################

+			# ------------------ PSS CORRELATION PROCESS AND DETECTION OF FRAME BOUNDARIES (B) ------------------#

+			######################################################################################################

+			

+			IQ_frame_main_norm = IQ_fullLTEframe_main[2*params.analysis_frame_len:3*params.analysis_frame_len]

+			ss_decoder.pss_run(IQ_frame_main_norm)

+								

+	

+			# ------------------ PSS CORRELATION PEAK DETECTION PROCESS (E) ------------------

+			

+			######################################################################################################

+			# ------------------ PSS CORRELATION PROCESS AND DETECTION OF FRAME BOUNDARIES (E) ------------------#

+			######################################################################################################

+			

+			

+				

+		    

+		  ######################################################################################

+			# ------------------ FREQUENCY-DOMAIN RESOURCE GRID PROCESSING (B) ------------------#

+			####################################################################################s##

+			if (ss_decoder.doIFFT == 1):

+				

+				# ------------------ IFFT PROCESS (B) ------------------

+				half_frame1D_TD = [0] * 5 * 14 * params.Nfft

+				

+				#print ['prev_PSS_idx = '+ str(hold_PSS_max_idx),'latest_PSS_idx = '+ str(PSS_Subframe_max_idx)]

+				

+				# Move peak index to 2nd subframe

+				hold_PSS_startIdx_2ndSubframe = params.analysis_frame_len + ss_decoder.PSS_Subframe_max_idx

+				ss_decoder.hold_PSS_max_idx = ss_decoder.PSS_Subframe_max_idx

+				

+				if (is_TDD):

+					PSS_subframe_start = (

+																# Move from tail to start of PSS seq

+																(hold_PSS_startIdx_2ndSubframe - (params.Nfft - 1))  

+																# Move to the begining of 2nd OFDM symbol)

+																-(params.NCP2) 

+															  # Move to the begining of 1st OFDM symbol

+															 	-(params.Nfft + params.NCP2)

+															 	# Move to the begining of 0th OFDM symbol

+															 	-(params.Nfft + params.NCP1) + 0

+															 )

+	

+				else:		#***FDD***

+					PSS_subframe_start = (

+																# Move from tail to start of PSS seq

+																(hold_PSS_startIdx_2ndSubframe - (params.Nfft - 1))  

+																# Move to begining of 6th OFDM symbol

+																-(params.NCP2)

+																# Move to begining of 1st OFDM symbol(from 6th to 1st)

+																-5*(params.Nfft + params.NCP2) 

+															 	# Move to the begining of 0th OFDM symbol

+															 	-(params.Nfft + params.NCP1) + 0	

+															 )

+				#dummy inits.

+				start_idx = 0

+				stop_idx = 0

+				

+				for fiveSubframe_iter in xrange(0, 5):		#Loop over half LTE frame

+					

+					for OFDMsym_iter in xrange(0, 14):			#Loop over OFDM symbols 

+						

+						if ((OFDMsym_iter == 0)or((OFDMsym_iter == 7)and(is_NR==0))):

+							NCP_len = params.NCP1

+						else:

+							NCP_len = params.NCP2

+						

+						start_idx = stop_idx + 1

+						

+						if ((OFDMsym_iter == 0)and(fiveSubframe_iter == 0)):

+							if (is_TDD):

+								start_idx = PSS_subframe_start - params.analysis_frame_len

+							else:

+								start_idx = PSS_subframe_start

+						

+						stop_idx = start_idx + (params.Nfft + NCP_len) - 1

+						start_idx_woCP = start_idx + NCP_len

+						

+						#print[start_idx,start_idx_woCP,stop_idx,stop_idx-start_idx,stop_idx-start_idx_woCP]

+							

+						begin_idx_OFDMsym = 14*params.Nfft*fiveSubframe_iter + params.Nfft*OFDMsym_iter

+						ending_idx_OFDMsym = 14*params.Nfft*fiveSubframe_iter + params.Nfft*(OFDMsym_iter+1)

+						

+						

+						half_frame1D_TD[begin_idx_OFDMsym:ending_idx_OFDMsym] = IQ_fullLTEframe_main[start_idx_woCP:stop_idx+1]

+						

+						pre_half_frame2D_FD_full = np.fft.fft(half_frame1D_TD[begin_idx_OFDMsym:ending_idx_OFDMsym])

+						pre_half_frame2D_FD_full[0] = 0.0

+						half_frame2D_FD_full[:,14*fiveSubframe_iter+OFDMsym_iter] = np.fft.fftshift(pre_half_frame2D_FD_full)

+						

+						#print np.amax(np.absolute(half_frame2D_FD_full[:,14*fiveSubframe_iter+OFDMsym_iter]))	

+				

+						# Map positive occupied subcarriers

+						#print len(half_frame2D_FD_occupiedRB[params.numRB/2:,14*fiveSubframe_iter+OFDMsym_iter])

+						half_frame2D_FD_occupiedRB[6*params.numRB:,14*fiveSubframe_iter+OFDMsym_iter] = pre_half_frame2D_FD_full[1:6*params.numRB+1]

+							

+						# Map negative occupied subcarriers	

+						half_frame2D_FD_occupiedRB[:6*params.numRB,14*fiveSubframe_iter+OFDMsym_iter] = pre_half_frame2D_FD_full[-6*params.numRB:]

+				# ------------------ IFFT PROCESS (E) ------------------

+				

+				

+				# ------------------ IDENTIFY LTE FRAME TIMING USING PREGEN. SSS SCRAMBLING CODES (B) ------------------

+				#print SSS_l_column

+				fetch_SSS_seq = half_frame2D_FD_occupiedRB[signals.SSS_k_index_start:signals.SSS_k_index_end+1,signals.SSS_l_column]

+				

+				half_subframe_offset = ss_decoder.lte_sss_run(fetch_SSS_seq)

+			

+				

+				# ------------------ IDENTIFY LTE FRAME TIMING USING PREGEN. SSS SCRAMBLING CODES (E) ------------------

+										 

+				

+				# ------------------ PILOTS PROCESSING (B) ------------------

+				if ((plotting_option == 2) or (plotting_option == 3)):

+					

+					#Extract cell-specific reference signals from occupied resource grid

+					Pilots_5subFrames_RAW = CSRS_extractionFunc(half_frame2D_FD_occupiedRB)

+					

+					#Generate pilots reference values 

+					refPilots_mat = genPilotsMat(half_subframe_offset)

+					

+					Pilot_GainPhaseComp_mat = genPilotGainPhaseComp_mat(Pilots_5subFrames_RAW,refPilots_mat)

+					CSRS_ChannelEst_RAW = CSRS_extractionFunc(Pilot_GainPhaseComp_mat)

+														

+					# ---- Compensation part ----

+					compMAT = genCompMat(Pilot_GainPhaseComp_mat,FIR_taps)	

+					

+					# ---- CFO computation ----

+					cfo_vec = CFO_calc(Pilot_GainPhaseComp_mat, NidCell)

+					

+					cfo_counter = cfo_counter+1

+					cumm_cfo_vec[1:,:] = cumm_cfo_vec[:-1,:] 

+					cumm_cfo_vec[0,:] = cfo_vec

+					

+					if (cfo_counter > 19):

+						avg_cfo_vec = np.sum(cumm_cfo_vec,axis=0)/win_avg

+					else: 			

+						avg_cfo_vec = np.sum(cumm_cfo_vec,axis=0)/cfo_counter

+					

+					avg_cfo_vec = np.reshape(avg_cfo_vec,(1,19))

+					

+					L.info( "cfo count : " + str(cfo_counter) )

+					IQ_Utils.CFO_stats_print(cfo_vec, avg_cfo_vec,cfo_counter)

+					

+					# Compensation

+					pilot_phase_compMAT = CSRS_extractionFunc(np.conjugate(compMAT))

+					pilot_phase_compMAT = np.divide(pilot_phase_compMAT,np.absolute(pilot_phase_compMAT))

+					Pilots_PhaseComp_Mat = np.multiply(Pilots_5subFrames_RAW,pilot_phase_compMAT)

+					#Pilots_PhaseComp_Mat = np.multiply(Pilots_5subFrames_RAW,PhaseDelta_mat)

+					

+					#full_phase_compMAT = np.divide(compMAT,np.absolute(compMAT))

+					#full_phaseComp_mat = np.multiply(half_frame2D_FD_occupiedRB,np.conjugate(full_phase_compMAT))

+					full_phaseComp_mat = np.divide(half_frame2D_FD_occupiedRB,compMAT)

+					Pilots_PhaseComp_Mat = CSRS_extractionFunc(full_phaseComp_mat)

+		

+				# ------------------ PILOTS PROCESSING (E) ------------------

+				

+			######################################################################################

+			# ------------------ FREQUENCY-DOMAIN RESOURCE GRID PROCESSING (E) ------------------#

+			######################################################################################

+		   

+			# ------------------ TIME DOMAIN PLOTS (B) ------------------

+			if ((plotting_option == 1) or (plotting_option == 2)):

+				ax_t_i_main.process(IQ_fullLTEframe_main)

+			# ------------------ TIME DOMAIN PLOTS (E) ------------------

+			

+			

+			# ------------------ FREQUENCY DOMAIN PLOTS (B) ------------------

+			if (plotting_option == 1):

+				ax_m.process(IQ_frame_main_norm)

+				

+				if (frame_counter == num_avg_frames):

+					frame_counter = 0

+					ax_m.reset()

+		

+			# ------------------ FREQUENCY DOMAIN PLOTS (E) ------------------

+			

+			

+			# ------------------ PSS CORRELATOR PLOTS (B) ------------------

+			if ((plotting_option == 1) or (plotting_option == 2)):

+				ax_pss_main.process(ss_decoder)

+			# ------------------ PSS CORRELATOR PLOTS (E) ------------------

+			

+			

+			# ------------------ LTE GRID PLOT (B) ------------------

+			if ((plotting_option == 1) or (plotting_option == 2) or (plotting_option == 3)):

+				if(ss_decoder.doIFFT == 1):

+					ax_5msFD_grid_main.process(half_frame2D_FD_occupiedRB)

+			# ------------------ LTE GRID PLOT (E) ------------------

+			

+			

+		  # ------------------ PILOTS PLOTS (B) ------------------	

+			if (plotting_option == 2):	

+				

+				if(ss_decoder.doIFFT == 1):

+					# Subframe 0 ************************************************	

+					ax_pilots_sf0_main.process(Pilots_5subFrames_RAW)

+					ax_pilots_phase_sf0_main.process(CSRS_ChannelEst_RAW)								

+					# Subframe 1 ************************************************

+					ax_pilots_sf1_main.process(Pilots_5subFrames_RAW)

+					ax_pilots_phase_sf1_main.process(CSRS_ChannelEst_RAW)

+					# Subframe 2 ************************************************	

+					ax_pilots_sf2_main.process(Pilots_5subFrames_RAW)

+					ax_pilots_phase_sf2_main.process(CSRS_ChannelEst_RAW)

+					# Subframe 3 ************************************************

+					ax_pilots_sf3_main.process(Pilots_5subFrames_RAW)

+					ax_pilots_phase_sf3_main.process(CSRS_ChannelEst_RAW)

+					# Subframe 4 ************************************************	

+					ax_pilots_sf4_main.process(Pilots_5subFrames_RAW)

+					ax_pilots_phase_sf4_main.process(CSRS_ChannelEst_RAW)

+						

+							

+			# ------------------ COMPENSATED DATA PLOTS (B) ------------------

+			if (plotting_option == 3):	

+			

+				if(ss_decoder.doIFFT == 1):

+					# Subframe 0 ************************************************	

+					ax_data_sf0.process(full_phaseComp_mat)					

+					# Subframe 1 ************************************************	

+					ax_data_sf1.process(full_phaseComp_mat)				

+					# Subframe 2 ************************************************	

+					ax_data_sf2.process(full_phaseComp_mat)				

+					# Subframe 3 ************************************************	

+					ax_data_sf3.process(full_phaseComp_mat)				

+					# Subframe 4 ************************************************	

+					ax_data_sf4.process(full_phaseComp_mat)				

+				

+			# ------------------ COMPENSATED DATA PLOTS (E) ------------------

+			

+			if(ss_decoder.doIFFT == 1):

+				plt.tight_layout()

+			plt.pause(1.0)

+			

+		######################################################################################

+		# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

+		# ++++++++++++++++++++++++++++ FRAME PROCESSING LOOP (E) ++++++++++++++++++++++++++++#

+		# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

+		######################################################################################

+		

+		

+		

+		

+		########################################################################

+		# ------------------ SCRIPT TERMINATION ROUTINES (B) ------------------#

+		########################################################################

+		

+		plt.show()

+		fr_MAIN.close()

+		L.logclose()

+		

+		########################################################################

+		# ------------------ SCRIPT TERMINATION ROUTINES (B) ------------------#

+		########################################################################

+		

+

+

+

+

+

+

+

+

+

+###################################################################################################

+#															    	  C O D E  P U R G A T O R Y																	#

+###################################################################################################

+

+

+################################# PSS CORRELATION ##############################################

+

+# ------------------ FREQUENCY-DOMAIN PSS CORRELATION (B) ------------------     

+	#  TD_LTEframe = complex_dataIn0(FrameLen*(ii-1)+1:FrameLen*ii);

+	#        

+	#  PSS1_corr_LTEframe2 = zeros(FrameLen,1); 

+	#  for jj=1:10

+	#      

+	#      TD_LTEsubframe = zeros(2048,1);

+	#      TD_LTEsubframe(1:1920) = TD_LTEframe(1920*(jj-1)+1:1920*jj);

+	#      FD_LTEsubframe = fft(TD_LTEsubframe,2048);

+	#      PSS1_corr_LTEsubframe = ifft(FD_LTEsubframe .* conj(PSS1_2048_FD),2048);

+	#      PSS1_corr_LTEframe2(1920*(jj-1)+1:1920*jj) = abs(PSS1_corr_LTEsubframe(1:1920)).^2;

+	#      

+	#  end

+  

+  #[max1_first_half_FD, idx1_first_half_FD] = max(PSS1_corr_LTEframe2);

+  #idx1_hold_vec_FD(file_size*(kk-1)+ii) = idx1_first_half_FD;

+  #if (max_hold < max1_first_half_FD)

+  #    max_hold = max1_first_half_FD;

+  #end

+  # ------------------ FREQUENCY-DOMAIN PSS CORRELATION (E) ------------------

+

+#	# Construct PSS correlation normaliztion vector (B)

+#	Rec_subframe_vec = IQ_ThreeframeBuffer_main[params.analysis_frame_len-params.Nfft+1:2*params.analysis_frame_len]

+#	print len(Rec_subframe_vec)

+#	IQ_frame_norm_main = [0] * params.analysis_frame_len

+#	for kk in xrange(0, params.analysis_frame_len):

+#		IQ_frame_norm_main[kk] = np.sum(np.square(Rec_subframe_vec[kk:kk+params.Nfft]))

+#		#print len(Rec_subframe_vec[kk:kk+params.Nfft])

+#		#print [kk,IQ_frame_norm_main[kk]]

+#	# Construct PSS correlation normaliztion vector (E)

+

+

+################################# 3D PLOTTING ##############################################

+#					ax_5msFD_grid_main.plot_surface(X, Y, Z,

+#																					cmap='jet', 

+#																					cstride=2,

+#																					rstride=2, 

+#																					linewidth=0, 

+#																					antialiased=True,

+#																					vmin=2.0, vmax=6.0)

+#					#ax_5msFD_grid_main.plot_surface(X, Y, Z,cmap=cm.jet, rstride=1, linewidth=0, antialiased=True,vmin=-5.0, vmax=1.0)		#LOG

+#					ax_5msFD_grid_main.set_zlim([0.0,10.0])

+#					ax_5msFD_grid_main.set_xlim([0.0,5*14-1])

+#					#ax_5msFD_grid_main.set_ylim([0.0,params.Nfft])

+#					ax_5msFD_grid_main.set_ylim([0.0,12*params.numRB])

+#					ax_5msFD_grid_main.view_init(elev=90, azim=-90)

+#					

+#					ax_5msFD_grid_main.w_xaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))

+#					#ax_5msFD_grid_main.w_xaxis.line.set_color((1.0, 1.0, 1.0, 0.0))

+#					ax_5msFD_grid_main.w_yaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))

+#					#ax_5msFD_grid_main.w_yaxis.line.set_color((1.0, 1.0, 1.0, 0.0))

+#					ax_5msFD_grid_main.set_zticks([])

+#					

+#					ax_5msFD_grid_main.dist = 6

+

+

+if __name__ == "__main__":

+	a = IQ_Input()

+	curr_dir_path = os.path.dirname(os.path.abspath(sys.argv[0]))

+	a.input_file_main = curr_dir_path + "\\IQ_datat\\LTE_INTP_DEMOD_P0P11_C1A1_TDD.txt"

+	a.input_time_file = curr_dir_path + ""

+	r = LTE_IQ_Analyzer()

+	r.run(a)		
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/NR_IQ_Analyzer.py b/mcu/tools/IQ_Analyzer/src/NR_IQ_Analyzer.py
new file mode 100644
index 0000000..7c6ddd7
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/NR_IQ_Analyzer.py
@@ -0,0 +1,451 @@
+#*****************************************************************************

+#  Copyright Statement:

+#  --------------------

+#  This software is protected by Copyright and the information contained

+#  herein is confidential. The software may not be copied and the information

+#  contained herein may not be used or disclosed except with the written

+#  permission of MediaTek Inc. (C) 2005

+#

+#  BY OPENING THIS FILE, BUYER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES

+#  THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")

+#  RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO BUYER ON

+#  AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,

+#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF

+#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.

+#  NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE

+#  SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR

+#  SUPPLIED WITH THE MEDIATEK SOFTWARE, AND BUYER AGREES TO LOOK ONLY TO SUCH

+#  THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. MEDIATEK SHALL ALSO

+#  NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE RELEASES MADE TO BUYER'S

+#  SPECIFICATION OR TO CONFORM TO A PARTICULAR STANDARD OR OPEN FORUM.

+#

+#  BUYER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND CUMULATIVE

+#  LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,

+#  AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,

+#  OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY BUYER TO

+#  MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.

+#

+#  THE TRANSACTION CONTEMPLATED HEREUNDER SHALL BE CONSTRUED IN ACCORDANCE

+#  WITH THE LAWS OF THE STATE OF CALIFORNIA, USA, EXCLUDING ITS CONFLICT OF

+#  LAWS PRINCIPLES.  ANY DISPUTES, CONTROVERSIES OR CLAIMS ARISING THEREOF AND

+#  RELATED THERETO SHALL BE SETTLED BY ARBITRATION IN SAN FRANCISCO, CA, UNDER

+#  THE RULES OF THE INTERNATIONAL CHAMBER OF COMMERCE (ICC).

+#

+#****************************************************************************/

+

+#*****************************************************************************

+#

+#* Filename:

+#* ---------

+#*   NR_IQ_Analyzer_dev.py

+#*

+#* Description:

+#* ------------

+#*   NR RF-DFE IQ Analyzer Tool

+#*

+#* Author:

+#* -------

+#*  Sini Kiprianoff (mtk14345)

+#*

+

+import os

+import sys

+import re

+import numpy as np

+

+from NR_IQ_Decoder import NR_SS_Decoder

+from IQ_Utils import L

+from IQ_Plots import NR_Plot

+from NR_IQ_Config import IQ_Input

+from NR_IQ_Config import NR_SignalParams

+from PBCH_decoder import PBCH_decoder

+

+import IQ_Utils

+

+import warnings

+warnings.filterwarnings('ignore')

+

+np.set_printoptions(threshold='nan')

+

+class NR_IQ_Analyzer:

+    def __init__(self):

+        print "Ready"

+

+    def run(self, iq_input):

+        ###########################################################

+        # ------------------ GLOBAL CONSTANTS (B) -----------------#

+        ############################################################

+

+        ## ------------------ INITIALIZE CLASSES (B) ------------------

+

+        params = NR_SignalParams(iq_input)

+        ss_decoder = NR_SS_Decoder(params, iq_input)

+        ss_decoder.allSymbols = []

+

+        ## ------------------ INITIALIZE CLASSES (E) ------------------

+

+        curr_dir_path = os.path.dirname(os.path.abspath(sys.argv[0]))

+

+        file_path_main = iq_input.input_file_main

+        fr_MAIN = open(file_path_main, "r+")

+

+        f_path = curr_dir_path + "\\prompt_traces.txt"

+        L.logopen(f_path)

+

+        ############################################################

+        # ------------------ GLOBAL CONSTANTS (E) -----------------#

+        ############################################################

+

+        # ------------------ FRAME PROCESSING LOOP CONSTANTS (B) ------------------

+        bitWidth_scale_factor = (1.0 / (params.Nfft))

+        half_frame2D_FD_occupiedRB = []

+

+        IQ_fullNRframe_main = [0] * 10 * params.analysis_frame_len

+        IQ_buffer_TD = []

+        half_frame2D_FD_occupiedRB = np.vectorize(complex)(np.zeros((12*params.numRB, params.symAmount*5)))

+        half_frame1D_TD = np.vectorize(complex)(np.zeros((12*params.numRB, params.symAmount*5)))

+

+        # ------------------ FRAME PROCESSING LOOP CONSTANTS (E) ------------------

+

+        ######################################################################################

+        # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

+        # ++++++++++++++++++++++++++++ FRAME PROCESSING LOOP (B) ++++++++++++++++++++++++++++#

+        # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

+        ######################################################################################

+

+        print "\n"

+        print "\n"

+        print "Filling the frame buffer"

+        for frame_iter in xrange(0, 7):

+

+            L.info("subframe count:" + str(frame_iter))

+

+            I_frame_main = [0] * params.analysis_frame_len

+            Q_frame_main = [0] * params.analysis_frame_len

+

+            line_counter = 0

+            i = 0          

+            inputdatatype = "unknown"

+            

+            for run_select in xrange(0, params.analysis_frame_len):

+

+                # ------------------ MAIN Antenna Frame Generation ------------------

+                line_main = fr_MAIN.readline()

+                

+                # Check if data is in correct format (Complex)

+                if ((line_main.find(";") == 0)):

+                    print "Skipping: " + str(line_main)

+                else:                   

+                    temp = line_main.split()

+                    value_counter = 0

+                    for value in temp:               

+                        matchObj = re.search("(\-*)(\d+\.*\d*)([\+\-])(\d+\.*\d*)i", value)

+                        if matchObj:

+                            inputdatatype = "complex"

+                            # Convert to complex value in Python, way #1

+                            real_sign = matchObj.group(1)

+                            real = matchObj.group(2)

+                            comp_sign = matchObj.group(3)

+                            comp = matchObj.group(4)

+                            I_frame_main[i] = float(real_sign + real)

+                            Q_frame_main[i] = float(comp_sign + comp)                        

+                            i = i + 1

+                    

+                    # Elseif data is not in correct format (complex) convert it to complex

+                        else:

+                            if not "unknown" in inputdatatype:

+                                print "### ERROR : Conflict in inputdatatype"

+                                raise                                

+

+                            value = value.rstrip('\n')        

+                            if value_counter == 0:                            

+                                if "0x" in value: # Hex

+                                    value.replace("0x", "")    #remove 0x

+                                    I_frame_main[line_counter] = IQ_Utils.twos_comp(int(value, 16), 8)

+                                else: # Dec

+                                    I_frame_main[line_counter] = float(value)    

+                            

+                            elif value_counter == 1:

+                                if "0x" in value: # Hex

+                                    value.replace("0x", "")    #remove 0x

+                                    Q_frame_main[line_counter] = IQ_Utils.twos_comp(int(value, 16), 8)

+                                else: # Dec

+                                    Q_frame_main[line_counter] = float(value)

+                            else:

+                                print " ### ERROR: Invalid format in input data"

+        

+                        value_counter = value_counter + 1

+                    line_counter = line_counter + 1

+

+            

+            # In case of NR CSD reference data, let's remove 4 symbols of extension after every subframe

+            # TODO: should be removed

+            for run_select in xrange(0, params.analysis_frame_overlap):

+                line_main = fr_MAIN.readline()

+

+            ##############################################################################

+            # ------------------ TIME-DOMAIN IQ SAMPLES PROCESSING (B) ------------------#

+            ##############################################################################

+            if iq_input.swap_iq:

+                I_frame_main_temp = I_frame_main

+                Q_frame_main_temp = Q_frame_main

+                I_frame_main = Q_frame_main_temp

+                Q_frame_main = I_frame_main_temp            

+

+            # Combine I and Q paths

+            IQ_frame_main = np.vectorize(complex)(I_frame_main, Q_frame_main)

+

+            IQ_fullNRframe_main[:9*params.analysis_frame_len] = IQ_fullNRframe_main[params.analysis_frame_len:]

+            IQ_fullNRframe_main[9*params.analysis_frame_len:] = IQ_frame_main

+            

+            IQ_buffer_TD.extend(IQ_frame_main)

+

+        print "Processing..."

+        for frame_iter in xrange(7, iq_input.tot_num_of_frames-1):

+

+            L.info("subframe count:" + str(frame_iter))

+

+            I_frame_main = [0] * params.analysis_frame_len

+            Q_frame_main = [0] * params.analysis_frame_len

+

+            line_counter = 0

+            i = 0

+            for run_select in xrange(0, params.analysis_frame_len):

+

+                # ------------------ MAIN Antenna Frame Generation ------------------

+                line_main = fr_MAIN.readline()

+                

+                # Check if data is in correct format (Complex)

+                if ((line_main.find(";") == 0)):

+                    print "Skipping: " + str(line_main)

+                else:                  

+                    temp = line_main.split()                 

+                    value_counter = 0

+                    

+                    for value in temp:                 

+                        matchObj = re.search("(\-*)(\d+\.*\d*)([\+\-])(\d+\.*\d*)i", value)

+                        if matchObj:

+                            inputdatatype = "complex"

+                            # Convert to complex value in Python

+                            real_sign = matchObj.group(1)

+                            real = matchObj.group(2)

+                            comp_sign = matchObj.group(3)

+                            comp = matchObj.group(4)

+                            I_frame_main[i] = float(real_sign + real)

+                            Q_frame_main[i] = float(comp_sign + comp)   

+                            i = i + 1

+                    

+                    # Elseif data is not in correct format (complex) convert it to complex

+                        else:

+                            if not "unknown" in inputdatatype:

+                                print "### ERROR : Conflict in inputdatatype"

+                                raise

+

+                            value = value.rstrip('\n')   

+                            if value_counter == 0:                            

+                                if "0x" in value: # Hex

+                                    value.replace("0x", "")    #remove 0x

+                                    I_frame_main[line_counter] = IQ_Utils.twos_comp(int(value, 16), 8)

+                                else: # Dec

+                                    I_frame_main[line_counter] = float(value)

+                            

+                            elif value_counter == 1:

+                                if "0x" in value: # Hex

+                                    value.replace("0x", "")    #remove 0x

+                                    Q_frame_main[line_counter] = IQ_Utils.twos_comp(int(value, 16), 8)

+                                else: # Dec

+                                    Q_frame_main[line_counter] = float(value)

+                            else:

+                                print " ### ERROR: Invalid format in input data"

+        

+                        value_counter = value_counter + 1

+                    line_counter = line_counter + 1

+            ##############################################################################

+            # ------------------ TIME-DOMAIN IQ SAMPLES PROCESSING (B) ------------------#

+            ##############################################################################

+            

+            if iq_input.swap_iq:

+                I_frame_main_temp = I_frame_main

+                Q_frame_main_temp = Q_frame_main

+                I_frame_main = Q_frame_main_temp

+                Q_frame_main = I_frame_main_temp

+

+            # Combine I and Q paths

+            IQ_frame_main = np.vectorize(complex)(I_frame_main, Q_frame_main)         

+               

+            IQ_fullNRframe_main[:9*params.analysis_frame_len] = IQ_fullNRframe_main[params.analysis_frame_len:]

+            IQ_fullNRframe_main[9*params.analysis_frame_len:] = IQ_frame_main

+

+            ######################################################################################################

+            # ------------------ PSS CORRELATION PROCESS AND DETECTION OF FRAME BOUNDARIES (B) ------------------#

+            ######################################################################################################

+

+            IQ_frame_main = IQ_fullNRframe_main[2*params.analysis_frame_len:3*params.analysis_frame_len]

+

+            if (iq_input.plotting_option[2] or iq_input.plotting_option[3]):

+                ss_decoder.pss_run(IQ_frame_main)    # Run PSS decoder

+                ss_decoder.sss_run()    # Run SSS decoder

+

+            ######################################################################################################

+            # ------------------ PSS CORRELATION PROCESS AND DETECTION OF FRAME BOUNDARIES (E) ------------------#

+            ######################################################################################################

+

+            ######################################################################################

+            # ------------------ FREQUENCY-DOMAIN RESOURCE GRID PROCESSING (B) ------------------#

+            ######################################################################################

+

+            # If PSS/SSS detected, find PBCH DMRS

+            if ss_decoder.PSS_peak_detected:

+                for i in xrange(len(ss_decoder.falsePSS_removed)):

+                    L.info("   PSS peak idx: " + str(ss_decoder.falsePSS_removed[i]))   # print indices of non false pss peaks

+                    L.info("   PSS peak max: " + str(ss_decoder.PSS_max_values_falseRemoved[i]))                    

+                    

+                    hold_PSS_startIdx_2ndSubframe = ss_decoder.falsePSS_removed[i] # False PSS peaks removed

+                    

+                    ss_decoder.DMRS_peak_detected = 0

+                    

+                    if ss_decoder.SSS_peak_detected:

+                        L.info("   SSS peak idx: " + str(ss_decoder.SSS_peaks[i]))

+                        L.info("   SSS peak max: " + str(ss_decoder.SSS_max_values[i]))

+

+                        # SSS position in a subframe

+                        hold_SSS_startIdx_2ndSubframe = ss_decoder.SSS_peaks[i] 

+                        

+                        # Extract DMRS at pilot locations 

+                        ssBlock = ss_decoder.fft_exact(hold_PSS_startIdx_2ndSubframe, hold_SSS_startIdx_2ndSubframe, ss_decoder.IQ_OneframeBuffer_main)

+                        fetched_dmrsREs = ss_decoder.dmrsExtraction(ssBlock)    

+

+                        # Run DMRS decoder 

+                        ss_decoder.dmrs_run(params.NidCell, fetched_dmrsREs)    

+                       

+                        if ss_decoder.DMRS_peak_detected:

+                            L.info("   Detected SSB idx: " + str(ss_decoder.ssbIdx))                      

+                            

+                            ssBlockRobust = ss_decoder.fft(hold_PSS_startIdx_2ndSubframe, hold_SSS_startIdx_2ndSubframe, ss_decoder.IQ_OneframeBuffer_main)                           

+                            ssBlockRobust = ss_decoder.calc_phaseConjugatedPilots(ssBlockRobust, ss_decoder.dmrsLocal)

+                                                                            

+                            fetched_dmrsREs = ss_decoder.dmrsExtraction(ssBlockRobust)    # Extract dmrs pilots

+                            ferror = ss_decoder.cfoCalc(fetched_dmrsREs, ss_decoder.dmrsLocal)     

+                                             

+                            print "Compensate frequency offset...."

+                            timeDomain_bufferCompensated = ss_decoder.fixCFOerror(ferror, ss_decoder.IQ_OneframeBuffer_main)     # Compensate CFO error           

+                            compensated_occupiedRB_FD = ss_decoder.fft(hold_PSS_startIdx_2ndSubframe, hold_SSS_startIdx_2ndSubframe, timeDomain_bufferCompensated)    # Do fft again                                                

+                            compensated_occupiedRB_FD = ss_decoder.calc_phaseConjugatedPilots(compensated_occupiedRB_FD, ss_decoder.dmrsLocal)          

+                            

+                            fetched_dmrsREs_cfoComp = ss_decoder.dmrsExtraction(compensated_occupiedRB_FD)    # Extract dmrs pilots

+                            ferror = ss_decoder.cfoCalc(fetched_dmrsREs_cfoComp, ss_decoder.dmrsLocal)    # Calculate CFO again

+                            

+                            # Estimate symbol timing offset 

+                            time_error = ss_decoder.stoEstCalc(fetched_dmrsREs_cfoComp)     

+                            

+                            # Correct estimated timing error in frequency domain                          

+                            compensated_occupiedRB_TD = ss_decoder.fixSTOerror(compensated_occupiedRB_FD, time_error)

+                                                                       

+                            # Pilot-based channel estimation

+                            channelEst = ss_decoder.channelEstimate(compensated_occupiedRB_TD)

+                            f_tilde = ss_decoder.detector(compensated_occupiedRB_TD, channelEst)

+                                                       

+                            if iq_input.plotting_option[4]: 

+                                A = 32      # number of bits in the information sequence

+                                list_size = 8       # list size for SCL decoding

+                                min_sum = False         # min sum approximation for SCL decoding or log-sum product     

+                                Lmax = iq_input.lmax

+                                ibar_SSB = ss_decoder.ssbIdx

+                                PBCH_decoder(f_tilde, A, list_size, min_sum, params.NidCell, Lmax, ibar_SSB)

+

+                    

+                    # ------------------ RESOURCE GRID PROCESS (B) ------------------

+       

+                        if iq_input.plotting_option[3] and ss_decoder.DMRS_peak_detected:

+                            half_frame1D_TD = [0] * 5 * params.symAmount * params.Nfft

+        

+                            PSS_subframe_start = ss_decoder.falsePSS_removed[0] - params.Nfft - 4    # Move from tail to start of PSS seq

+        

+                            start_idx = 0

+                            stop_idx = 0

+                            for half_frame_iter in xrange(0, 5):    # Loop over subframes

+                                for OFDMsym_iter in xrange(0, params.symAmount):    # Loop over OFDM symbols

+        

+                                    if OFDMsym_iter == 0:

+                                        NCP_len = params.NCP1

+                                    else:

+                                        NCP_len = params.NCP2

+        

+                                    if ((OFDMsym_iter == 0) and (half_frame_iter == 0)):

+                                        start_idx = PSS_subframe_start

+                                        stop_idx = start_idx + params.Nfft                                                              

+                                    else:

+                                        start_idx = stop_idx + NCP_len + 1

+                                        stop_idx = start_idx + params.Nfft

+        

+                                    #print[start_idx_woCP,stop_idx,stop_idx-start_idx_woCP]

+        

+                                    begin_idx_OFDMsym = params.symAmount*params.Nfft*half_frame_iter + params.Nfft*OFDMsym_iter

+                                    ending_idx_OFDMsym = params.symAmount*params.Nfft*half_frame_iter + params.Nfft*(OFDMsym_iter)

+        

+                                    half_frame1D_TD[begin_idx_OFDMsym:ending_idx_OFDMsym] = timeDomain_bufferCompensated[start_idx:stop_idx]

+        

+                                    pre_half_frame2D_FD_full = np.fft.fft(IQ_fullNRframe_main[start_idx:stop_idx])

+        

+                                    # Map positive occupied subcarriers

+                                    half_frame2D_FD_occupiedRB[6*params.numRB:, params.symAmount*half_frame_iter+OFDMsym_iter] = pre_half_frame2D_FD_full[:6*params.numRB]

+        

+                                    # Map negative occupied subcarriers

+                                    half_frame2D_FD_occupiedRB[:6*params.numRB, params.symAmount*half_frame_iter+OFDMsym_iter] = pre_half_frame2D_FD_full[-6*params.numRB:]

+    

+                                 #------------------ RESOURCE GRID PROCESS (E) ------------------

+

+            ######################################################################################

+            # ------------------ FREQUENCY-DOMAIN RESOURCE GRID PROCESSING (E) ------------------#

+            ######################################################################################

+ 

+

+        ######################################################################################

+        # ----------------------------------- PLOTTING (S) ----------------------------------#

+        ######################################################################################       

+   

+            

+        plot = NR_Plot(params)

+        if iq_input.plotting_option[1]:

+            plot.timedomain_plot(IQ_buffer_TD)

+        if iq_input.plotting_option[2]:

+            plot.ss_plot(ss_decoder)

+        if iq_input.plotting_option[3]:

+            plot.resourceGrid_plot(half_frame2D_FD_occupiedRB)

+        if iq_input.plotting_option[0]:

+            plot.freqdomain_plot(IQ_buffer_TD)

+         

+        detected_PBCH = len(ss_decoder.allSymbols)/432

+        if iq_input.plotting_option[4]:

+            if detected_PBCH < 9:

+                plot.constellation(ss_decoder.allSymbols[0:len(ss_decoder.allSymbols)], detected_PBCH, 5)              

+            else:

+                plot.constellation(ss_decoder.allSymbols[0:3456], detected_PBCH/2, 5)

+                plot.constellation(ss_decoder.allSymbols[3456:6912], detected_PBCH/2, 6)        

+              

+        plot.fig.show()

+        

+        if iq_input.plotting_option[2]:    

+            plot.pbchDMRS_plot(ss_decoder.ssbResults, ss_decoder.ssb_counter) 

+            plot.fig.show()

+         

+        

+

+        ######################################################################################

+        # ----------------------------------- PLOTTING (E) ----------------------------------#

+        ######################################################################################   

+        fr_MAIN.close()

+        print "### IQ Analyzer executed"

+        L.logclose()

+

+        ########################################################################

+        # ------------------ SCRIPT TERMINATION ROUTINES (B) ------------------#

+        ########################################################################

+

+if __name__ == "__main__":

+    

+    input_IQ = IQ_Input()

+    

+    r = NR_IQ_Analyzer()

+    r.run(input_IQ)

+

diff --git a/mcu/tools/IQ_Analyzer/src/NR_IQ_Config.py b/mcu/tools/IQ_Analyzer/src/NR_IQ_Config.py
new file mode 100644
index 0000000..516f75a
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/NR_IQ_Config.py
@@ -0,0 +1,84 @@
+import os

+import sys

+from NR_IQ_Signals import NR_SS_Signals

+

+# Input definition in case not defining those from GUI

+class IQ_Input:

+    def __init__(self):

+        # User interface input

+        self.NidCell = 0    # Cell ID

+        self.freq_range = 'sub 6-GHz'     # mmWave    

+        self.scs = 30        # sub-6 GHz: 15, 30 kHz. mmWave: 120,240 kHz

+        self.tot_num_of_frames = 40        # Subframes to be calculated

+        

+        curr_dir_path = os.path.dirname(os.path.abspath(sys.argv[0]))

+        

+        self.input_file_main = curr_dir_path + "\\IQ_datat\\iq_samples.txt"

+        self.plotting_option = [1, 1, 1, 1, 1]        # 1: frequency spectrum, 2: time domain, 3: pss/sss/dmrs, 4: resource grid, 5: PBCH

+        

+        # Extra config

+        self.frame_overlap = False                 # Frame overlap remove 

+        self.is_averagedFrames = True

+        self.is_avgSlidingWindow = True        

+        self.swap_iq = False

+

+      

+class NR_SignalParams:

+    def __init__(self, iq_input):   

+    

+        # Common attributes 

+        self.NidCell = iq_input.NidCell

+        if (iq_input.freq_range == 'sub 6-GHz'):

+            self.Lmax = 8

+        else:

+            self.Lmax = 64

+        

+        # Sampling rate specific configurations

+        if (iq_input.scs == 15):

+            self.analysis_frame_len = 3840

+            self.interp_freqSpectrum_lowLimit = -1.92*pow(10,6)

+            self.interp_freqSpectrum_upperLimit = 1.92*pow(10,6)

+            self.sample_rate = 3.84*pow(10,6)

+            self.symAmount = 14                        

+        elif (iq_input.scs == 30):

+            self.analysis_frame_len = 7680 

+            self.interp_freqSpectrum_lowLimit = -3.84*pow(10,6)

+            self.interp_freqSpectrum_upperLimit = 3.84*pow(10,6)        

+            self.sample_rate = 7.68*pow(10,6)

+            self.symAmount = 28           

+        elif (iq_input.scs == 120):

+            self.analysis_frame_len = 30720           

+            self.interp_freqSpectrum_lowLimit = -15.36*pow(10,6)

+            self.interp_freqSpectrum_upperLimit = 15.36*pow(10,6)    

+            self.sample_rate = 30.72*pow(10,6)

+            self.symAmount = 112       

+        elif (iq_input.scs == 240):

+            self.analysis_frame_len = 61440

+            self.interp_freqSpectrum_lowLimit = -30.72*pow(10,6)

+            self.interp_freqSpectrum_upperLimit = 30.72*pow(10,6)     

+            self.sample_rate = 61.44*pow(10,6)

+            self.symAmount = 224                    

+        else:

+            raise Exception('Unknown subcarrier spacing')

+                

+        self.Nfft = 256

+        self.numRB = 20                   # Maximum number of resource blocks in sync channel

+        self.num_avg_frames = 10

+    

+        self.NCP1 = 18      # cyclic prefix for first symbol     

+        self.NCP2 = 18      # cyclic prefix for subsequent symbols

+             

+        self.sample_time = 1/self.sample_rate

+        

+        if (iq_input.frame_overlap):    # frame overlap in case of NR CSD data

+            self.analysis_frame_overlap = 4*274-1 # 4 symbols of extension (total length 3840 + 4*274-1 = 4935)           

+        else:

+            self.analysis_frame_overlap = 0

+                    

+        # Initialize target signals 

+        self.signals = NR_SS_Signals(self.NidCell, self.Nfft)

+        

+        self.is_averagedFrames = iq_input.is_averagedFrames

+        self.is_avgSlidingWindow = iq_input.is_avgSlidingWindow

+        self.frames = iq_input.tot_num_of_frames            

+        
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/NR_IQ_Decoder.py b/mcu/tools/IQ_Analyzer/src/NR_IQ_Decoder.py
new file mode 100644
index 0000000..0fa4559
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/NR_IQ_Decoder.py
@@ -0,0 +1,603 @@
+import numpy as np

+import math

+

+from IQ_Utils import L

+from NR_IQ_Signals import NR_RS_Signals

+

+

+############################Class Declaration################################    

+class NR_SS_Decoder:

+    def __init__(self, params, iq_input):

+        self.params = params

+        self.signals = params.signals

+        self.iq_input = iq_input

+

+        self.PSS_corr_magSQR_output_frame_main = []

+        self.SSS_corr_magSQR_output_frame_main = []

+        

+        # PBCH-DMRS results

+        self.IQ_frame_DMRScorr_main  = []        

+

+        # We have found PSS singal

+        self.PSS_peak_detected = 0

+        # We have found SSS signal

+        self.SSS_peak_detected = 0

+        # We have found DMRS signal

+        self.DMRS_peak_detected = 0

+        # Index in our buffer, previous index

+        self.hold_PSS_max_idx = 0

+        self.hold_SSS_max_idx = 0

+        # Do Inverse Fast Fourier Transformation

+        self.doIFFT = 0

+        # Index on our index current.

+        self.PSS_Subframe_max_idx = 0

+        self.SSS_Subframe_max_idx = 0

+        self.PSS_Subframe_max_val = 0

+        self.SSS_Subframe_max_val = 0

+

+        # 3 Samples.

+        self.IQ_ThreeframeBuffer_main = [0] * 3 * params.analysis_frame_len

+        

+        # Number of symbols in a subframe

+        self.symAmount = params.symAmount

+       

+        # Case specific parameters

+        self.Nfft = params.Nfft

+        self.NCP1 = params.NCP1

+        self.sample_rate = params.sample_rate

+        self.numRB = params.numRB       

+

+        # PBCH-DMRS parameters

+        self.Lmax = params.Lmax     # max candidate beams

+        self.v = params.NidCell % 4     # frequency domain offset for dmrs

+        self.ssb_counter = 0

+        self.ssbResults = np.array([])    

+        self.allSymbols =[]

+

+        ##############################################################################

+        # ---------- DEFINE PILOT AND DATA CARRIER LOCATIONS IN PBCH (B) ------------#

+        ##############################################################################        

+        self.DMRS_pos13 = []    # DMRS in symbols #1 and #3

+        for i in xrange(0, 240, 4):

+            dmrsPos = i+self.v

+            self.DMRS_pos13.append(dmrsPos)

+

+        # DMRS symbol #2

+        self.DMRS_pos2 = [] 

+        for i in xrange(0, 48, 4):

+            dmrsPos = i+self.v

+            self.DMRS_pos2.append(dmrsPos)

+

+        for i in xrange(192, 240, 4):

+            dmrsPos = i+self.v

+            self.DMRS_pos2.append(dmrsPos)

+        

+        # Define SSS carriers

+        SSS_carriers = []        

+        for i in xrange(48, 192):

+            SSS_carriers.append(i)

+

+        # DMRS and SSS carriers in 3rd symbol

+        dmrs_SSS_carriers = SSS_carriers + self.DMRS_pos2    

+        dmrs_SSS_carriers.sort()

+        

+        allCarriers = np.arange(240)    # Incides of all subcarriers

+        # PBCH data carrier positions --> delete pilot carriers and SSS carriers

+        self.dataCarriers_sym13 = np.delete(allCarriers, self.DMRS_pos13)

+        self.dataCarriers_sym2 = np.delete(allCarriers, dmrs_SSS_carriers)      

+        ##############################################################################

+        # ---------- DEFINE PILOT AND DATA CARRIER LOCATIONS IN PBCH (E) ------------#

+        ##############################################################################             

+ 

+    ######################################################################

+    # Function to correlate received time domain samples with local PSS

+    # NOTE: N/A

+    # input: IQ subframe buffer

+    # output: Correlation peak locations and maximum peaks for PSS

+    ######################################################################     

+    def pss_run(self, IQ_frame_main_norm):

+        params = self.params

+

+        PSS_TD_seq = self.signals.PSS_TD_complex_pad_fftSize_wShift_ifft # Local

+        

+        # Keep feeding our 3 sample buffer, newest data to last

+        self.IQ_ThreeframeBuffer_main[:2*params.analysis_frame_len] = self.IQ_ThreeframeBuffer_main[params.analysis_frame_len:]

+        self.IQ_ThreeframeBuffer_main[2*params.analysis_frame_len:] = IQ_frame_main_norm

+        

+        self.IQ_OneframeBuffer_main = self.IQ_ThreeframeBuffer_main[params.analysis_frame_len:2*params.analysis_frame_len]

+

+        self.PSS_peak_detected = 0

+        self.PSS_peaks = []

+        self.PSS_peaks_values = []

+        self.pssSym = []

+        

+        startSym = 0

+        stopSym = startSym + params.Nfft + params.NCP2            

+        for i in xrange(params.symAmount):              

+            

+            # Normalize input sequences for cross-correlator

+            IQ_OneframeBuffer_main_norm = (self.IQ_OneframeBuffer_main[startSym:stopSym] - np.mean(self.IQ_OneframeBuffer_main[startSym:stopSym])) / (np.std(self.IQ_OneframeBuffer_main[startSym:stopSym]) * len(self.IQ_OneframeBuffer_main[startSym:stopSym]))

+            PSS_TD_seq = (PSS_TD_seq - np.mean(PSS_TD_seq)) / (np.std(PSS_TD_seq))                

+

+            # Cross-correlation with our reference signal to find reference signal

+            IQ_PSS_Corr = np.correlate(IQ_OneframeBuffer_main_norm, PSS_TD_seq, 'full')

+

+            # Convert our sample to abs.

+            IQ_PSS_Corr = np.absolute(IQ_PSS_Corr)

+

+            self.PSS_corr_magSQR_output_frame_main.extend(IQ_PSS_Corr)    # Fetch correlator results for plotting

+

+            self.PSS_Subframe_max_idx = np.argmax(IQ_PSS_Corr)     # Find maximum value from our data "Maximum Likehood"

+            self.PSS_Subframe_max_val = IQ_PSS_Corr[self.PSS_Subframe_max_idx]

+            

+            # Calculate average level

+            PSS_Subframe_mean = np.mean(IQ_PSS_Corr)

+

+            # Find peak

+            PSS_threshold = 0.35

+

+            if ((self.PSS_Subframe_max_val > 2.0*PSS_Subframe_mean)and(self.PSS_Subframe_max_val > PSS_threshold)):

+                self.PSS_peak_detected = 1

+                pss_peak = startSym + self.PSS_Subframe_max_idx

+                self.PSS_peaks.append(pss_peak)        # Collect PSS peaks in a one subframe

+                self.PSS_peaks_values.append(self.PSS_Subframe_max_val)

+                self.pssSym.append(i)                  # Save symbol number 

+

+            startSym = stopSym

+            stopSym = startSym + params.Nfft + params.NCP1

+

+    ######################################################################

+    # Function to correlate received time domain samples with local SSS

+    # NOTE: N/A

+    # input: -

+    # output: Correlation peak locations and maximum peaks for SSS

+    ###################################################################### 

+    def sss_run(self):

+        params = self.params

+

+        SSS_TD_seq = self.signals.SSS_TD_complex_pad_fftSize_wShift_ifft # Local

+               

+        self.SSS_peaks = []

+        self.falsePSS_removed = []

+        self.PSS_max_values_falseRemoved = []   # PSS max values after false removal

+        self.SSS_max_values = []    # SSS max values after false removal

+        

+        startSym = 0

+        stopSym = startSym + params.Nfft + params.NCP1            

+        for i in xrange(params.symAmount):               

+            

+            # Normalize input sequences for cross-correlator

+            IQ_OneframeBuffer_main_norm = (self.IQ_OneframeBuffer_main[startSym:stopSym] - np.mean(self.IQ_OneframeBuffer_main[startSym:stopSym])) / (np.std(self.IQ_OneframeBuffer_main[startSym:stopSym]) * len(self.IQ_OneframeBuffer_main[startSym:stopSym]))

+            SSS_TD_seq = (SSS_TD_seq - np.mean(SSS_TD_seq)) / (np.std(SSS_TD_seq))   

+             

+            # Cross-correlation with our reference signal to find reference signal

+            IQ_SSS_Corr = np.correlate(IQ_OneframeBuffer_main_norm, SSS_TD_seq, 'full')

+            

+            # Convert our sample to abs.

+            IQ_SSS_Corr = np.absolute(IQ_SSS_Corr)

+

+            self.SSS_corr_magSQR_output_frame_main.extend(IQ_SSS_Corr)    # Fetch correlator results for plotting

+

+            self.SSS_Subframe_max_idx = np.argmax(IQ_SSS_Corr) # Find maximum value from our data "Maximum Likehood"

+            self.SSS_Subframe_max_val = IQ_SSS_Corr[self.SSS_Subframe_max_idx]

+

+            # Calculate average level

+            SSS_Subframe_mean = np.mean(IQ_SSS_Corr)

+

+            # Find peak

+            SSS_threshold = 0.3

+

+            if ((self.SSS_Subframe_max_val > 2.0*SSS_Subframe_mean)and(self.SSS_Subframe_max_val > SSS_threshold)): 

+                sss_peak = startSym + self.SSS_Subframe_max_idx

+                self.SSS_peak_detected = 1

+

+                # Check if SSS symbol location 2 symbols away from PSS

+                for j in xrange(len(self.pssSym)):

+                    if ((i-self.pssSym[j]) == 2):

+                        self.SSS_peaks.append(sss_peak)

+                        self.falsePSS_removed.append(self.PSS_peaks[j])    # remove false alarms

+                        self.PSS_max_values_falseRemoved.append(self.PSS_peaks_values[j])

+                        self.SSS_max_values.append(self.SSS_Subframe_max_val)

+

+            startSym = stopSym 

+            stopSym = startSym + params.Nfft + params.NCP1

+

+    ######################################################################

+    # Function to correlate received PBCH-DMRS with local PBCH-DMRS to 

+    # get SSB index

+    # NOTE: N/A

+    # input: Received PBCH-DMRS, cell ID

+    # output: Correlation results to obtain SSB indices

+    ###################################################################### 

+    def dmrs_run(self, NidCell, dmrsREs):

+        self.DMRS_peak_detected = 0

+        Lmax = self.Lmax

+        dmrs_gen = NR_RS_Signals(NidCell)     # Init PBCH-DMRS reference signal

+        

+        # Create dmrs sequence from fetched pilots

+        dmrs_seq = [0]* 144

+        dmrs_seq[0:60] = dmrsREs[:,0]

+        dmrs_seq[60:84] = dmrsREs[0:24,1]

+        dmrs_seq[84:144] = dmrsREs[:,2]

+

+        n_hf_range = 1

+        for n_hf in xrange(n_hf_range):

+            for i_ssb in xrange(Lmax):

+

+                # Generate candidate dmrs sequence

+                dmrs_gen.run(i_ssb, n_hf)    

+                dmrsLocal = dmrs_gen.ref_sig_seq

+                                                

+                # Normalized cross-correlation to reference signal to find DMRS

+                dmrsREs_norm = (dmrs_seq - np.mean(dmrs_seq)) / (np.std(dmrs_seq) * len(dmrs_seq))

+                dmrsLocal_norm = (dmrsLocal - np.mean(dmrsLocal)) / (np.std(dmrsLocal))

+

+                IQ_frame_DMRScorr_main = np.correlate(dmrsREs_norm, dmrsLocal_norm, 'same')

+                IQ_frame_DMRScorr_main = np.absolute(IQ_frame_DMRScorr_main)

+

+                # Find max value

+                DMRS_Subframe_max_idx = np.argmax(IQ_frame_DMRScorr_main)

+                DMRS_Subframe_max_val = IQ_frame_DMRScorr_main[DMRS_Subframe_max_idx]

+                

+                # Calculate average level

+                dmrs_mean = np.mean(IQ_frame_DMRScorr_main)

+                

+                self.ssbResults = np.append(self.ssbResults, IQ_frame_DMRScorr_main) # Fetch correlation results for plotting

+                

+                DMRS_Threshold = 0.27

+                if ((DMRS_Subframe_max_val > 2.0*dmrs_mean) and (DMRS_Subframe_max_val > DMRS_Threshold)):      

+                    L.info( '   PBCH-DMRS peak max: ' + str(DMRS_Subframe_max_val))

+                    self.DMRS_peak_detected = 1

+                    self.ssbIdx = dmrs_gen.ii_ssb   

+                    self.dmrsLocal = dmrsLocal         # dmrs sequence match

+                    

+        self.ssb_counter = self.ssb_counter + 1

+

+    ######################################################################

+    # Function to fetch received PBCH-DMRS in one SS block

+    # NOTE: N/A

+    # input: SS block

+    # output: PBCH-DMRS pilots (raw)

+    ###################################################################### 

+    def dmrsExtraction(self, ssBlock):

+

+        pilotsRaw = np.vectorize(complex)(np.zeros((60, 3)))

+        offset = self.v  # DMRS frequency domain offset

+

+        j = 0

+        for pbchIdx in xrange(0, 4):

+            if pbchIdx == 1:

+                for i in xrange(0, 240, 4):

+                    pilotsRaw[j, 0] = ssBlock[i+offset, pbchIdx]

+                    j = j + 1

+            if pbchIdx == 2:

+                j = 0

+                for i in xrange(0, 48, 4):

+                    pilotsRaw[j, 1] = ssBlock[i+offset, pbchIdx]

+                    j = j + 1

+                for i in xrange(192, 240, 4):

+                    pilotsRaw[j, 1] = ssBlock[i+offset, pbchIdx]

+                    j = j + 1

+            if pbchIdx == 3:

+                j = 0

+                for i in xrange(0, 240, 4):

+                    pilotsRaw[j, 2] = ssBlock[i+offset, pbchIdx]

+                    j = j + 1

+

+        return pilotsRaw

+    

+    ######################################################################

+    # Function to have robust FFT (with cyclic prefix)

+    # NOTE: N/A

+    # input: PSS & SSS peak locations, time domain buffer

+    # output: Robust SS block

+    ###################################################################### 

+    def fft(self, hold_PSS_startIdx_2ndSubframe, hold_SSS_startIdx_2ndSubframe, timeDomainSamples):

+

+        Nfft = self.params.Nfft

+        CP_len = self.params.NCP1

+        numRB = self.params.numRB

+

+        # Locate symbol start positions

+        PSS_subframe_start = hold_PSS_startIdx_2ndSubframe - Nfft 

+        SSS_subframe_start = hold_SSS_startIdx_2ndSubframe - Nfft      

+        

+        ssBlock = np.vectorize(complex)(np.zeros((12*numRB, 4)))

+        

+        # dummy inits

+        start_idx_woCP = 0

+        stop_idx = 0          

+        for ssBlockSym_iter in xrange(0, 4):

+            if (ssBlockSym_iter == 0):

+                start_idx_woCP = PSS_subframe_start - 4

+                stop_idx = start_idx_woCP + Nfft    

+            

+            if (ssBlockSym_iter == 1):

+                start_idx_woCP = stop_idx + CP_len

+                stop_idx = start_idx_woCP + Nfft

+

+            if (ssBlockSym_iter == 2):

+                start_idx_woCP = SSS_subframe_start - 4

+                stop_idx = start_idx_woCP + Nfft

+

+            if (ssBlockSym_iter == 3):

+                start_idx_woCP = stop_idx + CP_len 

+                stop_idx = start_idx_woCP + Nfft

+              

+            pre_SSB_FD_full = np.fft.fft(timeDomainSamples[start_idx_woCP:stop_idx])

+

+            # Map positive occupied subcarriers

+            ssBlock[6*numRB:, ssBlockSym_iter] = pre_SSB_FD_full[:6*numRB]

+            # Map negative occupied subcarriers

+            ssBlock[:6*numRB, ssBlockSym_iter] = pre_SSB_FD_full[-6*numRB:]

+                                   

+        return ssBlock                      

+    

+    ######################################################################

+    # Function to have exact FFT (without cyclic prefix)

+    # NOTE: N/A

+    # input: PSS & SSS peak locations, time domain buffer

+    # output: Exact SS block

+    ###################################################################### 

+    def fft_exact(self, hold_PSS_startIdx_2ndSubframe, hold_SSS_startIdx_2ndSubframe, timeDomainSamples):

+

+        Nfft = self.params.Nfft

+        CP_len = self.params.NCP1

+        numRB = self.params.numRB

+        

+        # Locate symbol start positions

+        PSS_subframe_start = hold_PSS_startIdx_2ndSubframe - Nfft

+        SSS_subframe_start = hold_SSS_startIdx_2ndSubframe - Nfft

+        dmrs_subframe_start = hold_PSS_startIdx_2ndSubframe + CP_len

+        dmrs2_subframe_start = hold_PSS_startIdx_2ndSubframe + CP_len + Nfft + CP_len + Nfft + CP_len

+        

+        ssBlock = np.vectorize(complex)(np.zeros((12*numRB, 4)))

+        

+        # dummy inits            

+        start_idx_woCP = 0

+        stop_idx = 0   

+        for ssBlockSym_iter in xrange(4):

+

+            if (ssBlockSym_iter == 0):

+                start_idx_woCP = PSS_subframe_start 

+                stop_idx = start_idx_woCP + Nfft    

+                                                                     

+            if (ssBlockSym_iter == 1):                        

+                start_idx_woCP = dmrs_subframe_start + 1

+                stop_idx = start_idx_woCP + Nfft    

+    

+            if (ssBlockSym_iter == 2):

+                start_idx_woCP = SSS_subframe_start

+                stop_idx = start_idx_woCP + Nfft

+                            

+            if (ssBlockSym_iter == 3):

+                start_idx_woCP = dmrs2_subframe_start + 1

+                stop_idx = start_idx_woCP + Nfft

+            

+            pre_SSB_FD_full = np.fft.fft(timeDomainSamples[start_idx_woCP:stop_idx])            

+

+            # Map positive occupied subcarriers

+            ssBlock[6*numRB:, ssBlockSym_iter] = pre_SSB_FD_full[:6*numRB]            

+            # Map negative occupied subcarriers 

+            ssBlock[:6*numRB, ssBlockSym_iter] = pre_SSB_FD_full[-6*numRB:]                  

+        

+        return ssBlock  

+

+    ######################################################################

+    # Function to multiply received pilots with local conjugates in one 

+    # SS block

+    # NOTE: N/A

+    # input: SS block, local DMRS

+    # output: SS block 

+    ######################################################################     

+    def calc_phaseConjugatedPilots(self, ssBlock, dmrsLocal):

+        

+        count = 0

+        for i in xrange(4): 

+            if i == 1:

+                for j in xrange(len(ssBlock[:, 1])):  

+                    if (count < 60):

+                        if (self.DMRS_pos13[count] == j):

+                            ssBlock[j,i] = ssBlock[j,i]*np.conjugate(dmrsLocal[count])  

+                            count = count + 1

+            count = 0            

+            if i == 2:

+                for j in xrange(len(ssBlock[:, 2])):

+                    if (count < 24):

+                        if (self.DMRS_pos2[count] == j):

+                            ssBlock[j,i] = ssBlock[j,i]*np.conjugate(dmrsLocal[60+count]) 

+                            count = count + 1

+           

+            count = 0 

+            if i == 3:

+                for j in xrange(len(ssBlock[:, 3])):

+                    if (count < 60):

+                        if (self.DMRS_pos13[count] == j):

+                            ssBlock[j,i] = ssBlock[j,i]*np.conjugate(dmrsLocal[84+count]) 

+                            count = count + 1   

+                            

+        return ssBlock     

+  

+    ######################################################################

+    # Function to calculate frequency offset using pilot signals

+    # NOTE: N/A

+    # input: Received & known PBCH-DMRS

+    # output: Frequency error

+    ######################################################################

+    def cfoCalc(self, pilotsRaw, known_dmrsREs):

+        sym1_dmrs = pilotsRaw[:,0]

+        sym3_dmrs = pilotsRaw[:,2]     

+        self.sym1_dmrs = sym1_dmrs

+        self.sym1_dmrs = sym3_dmrs

+

+        cfoTotal = 0

+        for i in xrange(0, 60):

+            cfo = sym1_dmrs[i] * np.conjugate(sym3_dmrs[i])

+            cfoTotal = cfoTotal + cfo

+

+        cfoAngle = np.angle(cfoTotal)

+        symbolLenWithCp = self.Nfft + self.NCP1

+        ferror = -cfoAngle/(2*math.pi)*self.sample_rate/(symbolLenWithCp*2)

+        L.info("   Frequency offset (Hz): " + str(ferror))

+        

+        return ferror

+    

+    ######################################################################

+    # Function to compensate frequency error in time domain

+    # NOTE: N/A

+    # input: Frequency error, time domain input buffer

+    # output: Compensated time domain buffer

+    ######################################################################

+    def fixCFOerror(self, ferror, inputBuffer):

+        time_vec = np.divide(np.arange(len(inputBuffer)), self.params.sample_rate)

+        compensatedBuffer = np.multiply(inputBuffer, np.exp(2*math.pi*1j*time_vec*-ferror))

+

+        return compensatedBuffer

+        

+    ######################################################################

+    # Function to calculate symbol timing offset using pilot signals 

+    # NOTE: N/A

+    # input: Received PBCH-DMRS 

+    # output: Time error

+    ######################################################################         

+    def stoEstCalc(self, pilotsRaw):

+

+        dmrs1 = self.sym1_dmrs

+        dmrs3 = self.sym1_dmrs

+

+        error1 = error2 = 0

+        for i in xrange(1,len(dmrs1)):

+            sto = np.multiply(dmrs1[i-1], np.conjugate(dmrs1[i]))

+            error1 = sto + error1

+

+        for i in xrange(1,len(dmrs3)):

+            sto = np.multiply(dmrs3[i-1], np.conjugate(dmrs3[i]))

+            error2= sto + error2

+

+        stoTot = error1 + error2

+        time_error = np.angle(stoTot)

+        time_error = np.divide(time_error, 4)

+        L.info("   Timing offset (rad): " + str(time_error))

+

+        return time_error

+        

+    ######################################################################

+    # Function to fix time error in frequency domain

+    # NOTE: N/A

+    # input: SS block, time error

+    # output: Corrected SS block

+    ######################################################################  

+    def fixSTOerror(self, ssBlock, time_error):

+

+        ssBlock_corr = np.vectorize(complex)(np.zeros((12*self.params.numRB, 4)))

+        for i in xrange(4):

+            ssBlock_corr[:,i] = np.multiply(ssBlock[:,i], np.exp(1j*time_error*np.arange(len(ssBlock[:,0]))))

+

+        return ssBlock_corr

+

+    ######################################################################

+    # Function to estimate channel using timing & frequency averaging 

+    # approach inside SS block

+    # NOTE: N/A

+    # input: Time and frequency corrected SS block 

+    # output: Channel estimate 

+    ######################################################################  

+    def channelEstimate(self, compensatedBlock_time):

+

+        sym1_corr = compensatedBlock_time[:, 1]

+        sym2_corr = compensatedBlock_time[:, 2]

+        sym3_corr = compensatedBlock_time[:, 3]

+  

+        ch = [0] * 60        

+        count = 0

+        for k in xrange(len(ch)):   # First average over time at DMRS positions

+            scale = 2   

+            ch[k] = sym1_corr[self.DMRS_pos13[k]]

+

+            if (self.DMRS_pos2[count] == self.DMRS_pos13[k]):

+                ch[k] = ch[k] + sym2_corr[self.DMRS_pos2[count]]

+                scale = 3

+                count = count + 1

+

+            ch[k] = ch[k] + sym3_corr[self.DMRS_pos13[k]]

+            ch[k] = ch[k]/scale

+

+        out = [0] * 240

+        for k in xrange(len(ch)-1):   # Then, average over frequency

+            out[self.DMRS_pos13[k]+0] = (ch[k] + ch[k+1]) / 2

+            out[self.DMRS_pos13[k]+1] = (ch[k] + ch[k+1]) / 2

+            out[self.DMRS_pos13[k]+2] = (ch[k] + ch[k+1]) / 2

+            out[self.DMRS_pos13[k]+3] = (ch[k] + ch[k+1]) / 2

+

+        # Check positions at low and high frequencies if channel estimate needed

+        for k in xrange(self.DMRS_pos13[0]+1):

+            out[k] = ch[0]

+

+        for k in xrange(self.DMRS_pos13[len(ch)-1]-1,239):

+            out[k+1] = ch[len(ch)-1]

+

+        return out

+

+    ######################################################################

+    # Function to get transmitted soft bits and demodulate coherently

+    # NOTE: N/A

+    # input: SS block, Channel estimate

+    # output: Decoded Log Likelihood Ratio sequence (length 864)

+    ######################################################################  

+    def detector(self, block, ch):

+

+        pbchSym13_pos = self.dataCarriers_sym13

+        pbchSym2_pos = self.dataCarriers_sym2

+    

+        dataSym13 = np.vectorize(complex)(np.zeros((len(pbchSym13_pos), 2)))

+        dataSym2 = np.vectorize(complex)(np.zeros((len(pbchSym2_pos), 1)))

+        

+        E = 864 # Rate matching output sequence length 38.212: 7.1.5

+        llr_sequence = np.zeros((E,1))

+        

+        for j in xrange(len(pbchSym13_pos)):

+            dataSym13[j,0] = block[pbchSym13_pos[j], 1]                       

+            dataSym13[j,1] = block[pbchSym13_pos[j], 3]

+                

+        for j in xrange(len(pbchSym2_pos)):            

+            dataSym2[j,0] = block[pbchSym2_pos[j], 2]    

+                        

+        scale = np.mean(np.abs(ch))  # channel scaled

+        count = count2 = 0

+        llr_count_sym1 = llr_count_sym2 = llr_count_sym3 = 0

+        offset_sym3 = 2*len(pbchSym13_pos) + 2*len(pbchSym2_pos)

+        offset_sym2 = 2*len(pbchSym13_pos)

+

+        for k in xrange(len(ch)):

+                # Scale symbols and LLR

+                ch_angle = np.angle(ch[k])      # channel angle

+                rot = np.exp(-1j*ch_angle)

+                ch_rot = np.real(ch[k]*rot/scale)       # rotate channel to real and scale to one                

+                

+                if k == pbchSym13_pos[count]:   

+                    for i in xrange(2):

+                        dataSym13_rot = dataSym13[count,i]*rot/scale*math.sqrt(2)      # scale data symbol

+                        self.allSymbols.append(dataSym13_rot)

+                        

+                        if(i==0):

+                            llr_sequence[llr_count_sym1] = np.power((ch_rot-(np.real(dataSym13_rot))), 2) - np.power((ch_rot+(np.real(dataSym13_rot))), 2)   # real part

+                            llr_sequence[llr_count_sym1 + 1] = np.power((ch_rot-(np.imag(dataSym13_rot))), 2) - np.power((ch_rot+(np.imag(dataSym13_rot))), 2)  # imag part

+                            llr_count_sym1 = llr_count_sym1 + 2

+                            

+                        if(i==1):

+                            llr_sequence[offset_sym3 + llr_count_sym2] = np.power((ch_rot-(np.real(dataSym13_rot))), 2) - np.power((ch_rot+(np.real(dataSym13_rot))), 2)    # real part 

+                            llr_sequence[offset_sym3 + llr_count_sym2 + 1] = np.power((ch_rot-(np.imag(dataSym13_rot))), 2) - np.power((ch_rot+(np.imag(dataSym13_rot))), 2)    # imag part 

+                            llr_count_sym2 =  llr_count_sym2 + 2

+                                

+                    count = count + 1                                        

+                                        

+                if k == pbchSym2_pos[count2]:

+                        dataSym2_rot = dataSym2[count2,0]*rot/scale*math.sqrt(2)   

+                        self.allSymbols.append(dataSym2_rot)                        

+                        

+                        llr_sequence[offset_sym2 + llr_count_sym3] = np.power(ch_rot-np.real(dataSym2_rot), 2) - np.power(ch_rot+np.real(dataSym2_rot), 2)  # real part 

+                        llr_sequence[offset_sym2 + llr_count_sym3 + 1] = np.power(ch_rot-np.imag(dataSym2_rot), 2) - np.power(ch_rot+np.imag(dataSym2_rot), 2)  # imag part

+                        llr_count_sym3 = llr_count_sym3 + 2

+                        count2 = count2 + 1

+

+        return llr_sequence

diff --git a/mcu/tools/IQ_Analyzer/src/NR_IQ_Signals.py b/mcu/tools/IQ_Analyzer/src/NR_IQ_Signals.py
new file mode 100644
index 0000000..d390c27
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/NR_IQ_Signals.py
@@ -0,0 +1,142 @@
+import math

+import numpy as np

+

+class NR_SS_Signals:

+    def __init__(self, NidCell, Nfft):

+        

+        PSS_FFT_size = Nfft

+        # Compute physical layer identity Nid2 in range [0:2]

+        Nid2 = NidCell % 3

+        self.Nid2 = Nid2

+        # Compute physical layer cell-identity group Nid1 (range NR [0:335])

+        Nid1 = math.floor(NidCell / 3.0)

+        

+        ## ------------------ PSS INIT (B) ------------------

+        ## 38.211 7.4.2.2.1

+        print "Generating Primary Synchronization Signal....",

+

+        # Compute d_pss(n) sequence for the PSS

+

+        # Calculate m

+        # 0<=n<127

+        m = [0] * 127        

+        for n in xrange(0, 127):

+            m[n] = (n + 43*Nid2) % 127

+

+        x_m = [0] * 127

+        x_m[6] = x_m[5] = x_m[4] = x_m[2] = x_m[1] = 1            

+        # Calculate x(m)

+        for i in xrange(0, (127-7)):

+            x_m[i+7] = (x_m[i+4] + x_m[i]) % 2

+

+        # Calculate d_pss(n)

+        d_pss = [0] * 127        

+        for n in xrange(0, 127):

+            d_pss[n] = 1-2*x_m[m[n]]

+

+        self.PSS_TD_complex_pad = d_pss

+        self.PSS_TD_complex_pad_fftSize_wShift = [0] * PSS_FFT_size

+        self.PSS_TD_complex_pad_fftSize_wShift[0:63] = self.PSS_TD_complex_pad[64:]

+        self.PSS_TD_complex_pad_fftSize_wShift[PSS_FFT_size-64:] = self.PSS_TD_complex_pad[:64]

+        self.PSS_TD_complex_pad_fftSize_wShift_ifft = np.fft.ifft(self.PSS_TD_complex_pad_fftSize_wShift)

+        print "Done."        

+        ## ------------------ PSS INIT (E) ------------------

+

+        ## ------------------ SSS INIT (B) ------------------

+        ## 38.211 7.4.2.3.1

+        print "\n"

+        print "Generating Secondary Synchronization Signal....",

+

+        # Compute d_sss(n) sequence for the SSS

+

+        # Calculate x0 and x1

+        x_0 = [0] * 127

+        x_0[0] = 1

+        x_1 = [0] * 127

+        x_1[0] = 1        

+        for i in xrange(0,(127-7)):

+            x_0[i+7] = (x_0[i+4] + x_0[i]) % 2

+            x_1[i+7] = (x_1[i+1] + x_1[i]) % 2

+

+        # Calculate m0 and m1

+        # 0 <= m0 <= 40

+        # 0 <= m1 <= 111

+        m_0 = int(15 * math.floor(Nid1/112) + 5 * Nid2)

+        m_1 = int(Nid1 % 112)

+        

+        # Calculate d_sss(n)

+        self.d_sss = [0] * 127        

+        for n in xrange(0,127):

+            self.d_sss[n] = (1-2*x_0[(n+m_0) % 127])*(1-2*x_1[(n+m_1) % 127])

+            #print"d_sss[",n,"] = ",self.d_pss[n]

+

+        #Define indices location of SSS in NR resource grid

+        self.SSS_k_index_start = 56

+        self.SSS_k_index_end = 182

+        self.SSS_l_column = 2

+

+        self.SSS_TD_complex_pad = self.d_sss

+        self.SSS_TD_complex_pad_fftSize_wShift = [0] * PSS_FFT_size

+        self.SSS_TD_complex_pad_fftSize_wShift[0:63] = self.SSS_TD_complex_pad[64:]

+        self.SSS_TD_complex_pad_fftSize_wShift[PSS_FFT_size-64:] = self.SSS_TD_complex_pad[:64]

+        self.SSS_TD_complex_pad_fftSize_wShift_ifft = np.fft.ifft(self.SSS_TD_complex_pad_fftSize_wShift)

+        print "Done."    

+

+

+class NR_RS_Signals:

+    def __init__(self, NidCell):

+        

+        ## ------------------ DMRS PBCH INIT (B) ------------------

+        ## 38.211 7.4.1.4.1        

+        

+        # Common attributes

+        self.Nc = 1600

+        self.Mpn = 288      

+        self.NidCell = NidCell

+

+        ## ------------------ DMRS PBCH INIT (E) ------------------        

+                

+    def run(self, i_ssb, n_hf):

+

+        # Scrambling sequence generator initialization 

+        ii_ssb = i_ssb + 4 * n_hf

+        self.ii_ssb = ii_ssb

+        c_init = math.pow(2, 11) * (ii_ssb + 1) * ((self.NidCell / 4) + 1) + math.pow(2, 6) * (ii_ssb + 1) + (self.NidCell % 4)

+        

+        # Pseudo-random sequence generation (length-31 Gold sequence)        

+        # x1 initialization

+        x1 = [0] * (self.Mpn + self.Nc + 31)

+        x1[0] = 1

+        # Initialize x2 using c_init

+        x2 = [0] * (self.Mpn + self.Nc + 31)

+        for i in xrange(0, 31):

+            x2_val = math.floor(c_init/math.pow(2, i))

+            x2[i] = x2_val

+            

+        # Generation of x1(n) and x2(n) m-sequence

+        for n in xrange(0, self.Mpn + self.Nc):

+            x1[n + 31] = (x1[n + 3] + x1[n]) % 2

+            x2[n + 31] = (x2[n + 3] + x2[n + 2] + x2[n + 1] + x2[n]) % 2

+        

+        # Generation of Gold sequence

+        c_seq = [0] * self.Mpn

+        for n in xrange(0, self.Mpn):

+            c_seq[n] = (x1[n + self.Nc] + x2[n + self.Nc]) % 2        

+

+        # I and Q components in c-sequence

+        c_seq_Icomp = c_seq[0:self.Mpn:2]

+        c_seq_Qcomp = c_seq[1:self.Mpn:2]

+

+        c_seq_Icomp[:] = [1 - i * 2 for i in c_seq_Icomp]

+        c_seq_Qcomp[:] = [1 - i * 2 for i in c_seq_Qcomp]

+                

+        # Generation of QPSK based RS

+        ref_sig_seq = 1/np.sqrt(2) * np.vectorize(complex)(c_seq_Icomp, c_seq_Qcomp)

+

+        self.ref_sig_seq = ref_sig_seq

+        

+        return ref_sig_seq

+        

+        

+        

+        
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/PBCH_decoder.py b/mcu/tools/IQ_Analyzer/src/PBCH_decoder.py
new file mode 100644
index 0000000..3afd99e
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/PBCH_decoder.py
@@ -0,0 +1,167 @@
+from get_3GPP_N import get_3GPP_N

+from get_3GPP_crc_interleaver_pattern import get_3GPP_crc_interleaver_pattern

+from get_3GPP_rate_matching_pattern import get_3GPP_rate_matching_pattern

+from get_3GPP_sequence_pattern import get_3GPP_sequence_pattern

+from get_3GPP_info_bit_pattern import get_3GPP_info_bit_pattern

+from DCKA_polar_decoder import DCKA_polar_decoder

+from Scrambler import Scrambler

+import IQ_Utils

+

+class MIB_Parser:

+#####################################################

+#    38.331. 6.2.2

+#    MIB ::=                             SEQUENCE {

+#    systemFrameNumber                   BIT STRING (SIZE (6)),                          6 bit

+#    subCarrierSpacingCommon             ENUMERATED {scs15or60, scs30or120},             1 bit

+#    ssb_SubcarrierOffset                INTEGER (0..15),                                4 bit

+#    dmrs_TypeA_position                 ENUMERATED {pos2, pos3},                        1 bit 

+#    pdcchConfigSIB1                     PDCCH-ConfigSIB1,                               8 bit 

+#    cellBarred                          ENUMERATED {barred, notBarred},                 1 bit

+#    intraFreqReselection                ENUMERATED {allowed, notAllowed},               1 bit

+#    spare                               BIT STRING (SIZE (1))                           1 bit

+#

+#    

+#pdcchConfigSIB1 ::=                SEQUENCE {

+#    controlResourceSetZero              ControlResourceSetZero,

+#    searchSpaceZero                     SearchSpaceZero

+#}

+#####################################################

+

+    def __init__(self, a_hat, Lmax):

+        

+        ############# MIB fields parsing for frequency range 1 #############

+        mibbits = a_hat

+        mibbits = ''.join(map(str, mibbits))

+        mibbits = mibbits[::-1] #reverse

+        self.reservedBits = IQ_Utils.bi2de(mibbits[0:2])

+        self.k_ssb = mibbits[2]

+        self.HRF = mibbits[3]

+        sfnCombinePHYRRC = mibbits[25:31] + mibbits[4:8]

+        self.systemFrameNumber = IQ_Utils.bi2de(sfnCombinePHYRRC) 

+        

+        if Lmax == 64:

+            commonSCSs = [60, 120]

+            

+        else:

+            commonSCSs = [15, 30]

+          

+        self.RAN2_sparedBit = mibbits[8]

+        self.intraFreqSelection = mibbits[9]

+        self.cellBarred = mibbits[10]

+        self.pdcchConfigSIB1 = IQ_Utils.bi2de(mibbits[11:19])    

+        self.dmrs_TypeA_position = 2 + int(mibbits[19])

+        self.ssb_SubcarrierOffset = IQ_Utils.bi2de(mibbits[20:24])    

+        self.subCarrierSpacingCommon = commonSCSs[int(mibbits[24])]

+        self.msgIndicator = mibbits[31]

+        

+        print "NFrame: ", self.systemFrameNumber

+        print "SubcarrierSpacingCommon: ", self.subCarrierSpacingCommon

+        print "ssb-SubcarrierOffset: ", self.ssb_SubcarrierOffset

+        print "DL-DMRS_typeA_pos: ", self.dmrs_TypeA_position

+        print "PDCCHConfigSIB1: ", self.pdcchConfigSIB1

+        print "CellBarred: ", self.cellBarred

+        print "IntraFreqSelection: ", self.intraFreqSelection

+        print "Spare: ", self.RAN2_sparedBit

+        print "k_ssb", self.k_ssb

+        print "HRF: ", self.HRF

+        print "reservedBits:", self.reservedBits 

+

+##############################################################################

+# NR PBCH payload interleaving 

+# NOTE: N/A

+# input: 

+#        payload (32-bit)

+# output: 

+#         interleaved payload (32-bit)

+##############################################################################

+

+def get_3GPP_PBCH_payload_intlv(payload_bit):

+        

+    mib_intlv_pattern = [1,16,23,18,17,8,30,4,9,11,12,13,14,15,19,20,21,22,25,

+               26,27,28,29,31,10,6,24,7,0,5,3,2]

+    

+    intlv_out = [0]*32

+    for idx in xrange(31,-1,-1):

+        intlv_out[idx] = payload_bit[mib_intlv_pattern[idx]]

+

+    return intlv_out     

+

+#####################################################################################################################################

+# PBCH_DECODER Polar decoder for the Public Broadcast Channel (PBCH) of 3GPP New Radio, as defined in Section 7.1 of TS38.212. 

+# Implements the Cyclic Redudancy Check (CRC) attachment of Section 7.1.3, the channel coding of Section 7.1.4 and the rate 

+# matching of Section 7.1.5.  

+# NOTE: This code does not implement the payload generation of Section 7.1.1 or the scrambling of Section 7.1.2. Function decodes 

+# the encoded LLR sequence f_tilde, in order to obtain the recovered information bit sequence a_hat.

+# input: 

+#        f_tilde should be a real row vector comprising 864 Logarithmic Likelihood Ratios (LLRS), each having a value obtained 

+#        as LLR = ln(P(bit=0)/P(bit=1)). The first LLR corresponds to f_0 from Section  7.1.5 of TS38.212, while the last LLR 

+#        corresponds to f_E-1.

+#        A should be 32. It specifies the number of bits in the information bit sequence.       

+#        L should be a scalar integer. It specifies the list size to use during Successive Cancellation List (SCL) decoding.

+#        min_sum shoular be a scalar logical. If it is true, then the SCL decoding process will be completed using the min-sum 

+#        approximation. Otherwise, the log-sum-product will be used. The log-sum-product gives better error correction capability 

+#        than the min-sum, but it has higher complexity.

+#        Lmax is number of SSB candidates in a burst.

+# output: 

+#         a_hat will be a binary row vector comprising 32 bits, each having the value 0 or 1. The first output bit corresponds

+#         to a'_0 from Section 7.1.3 of TS38.212, while the last output bit corresponds to a'_A-1.

+#####################################################################################################################################

+def PBCH_decoder(f_tilde, A, list_size, min_sum, NidCell, Lmax, ibar_SSB):

+

+    E = len(f_tilde)

+    

+    # A is always 32 in PBCH

+    if A != 32:

+        raise Exception('polar_3gpp_matlab:UnsupportedBlockLength, A should be 32.')

+    

+    # E is always 864 in PBCH

+    if E != 864:

+        raise Exception('polar_3gpp_matlab:UnsupportedBlockLength, E should be 864.')

+    

+    # The CRC polynomial used in 3GPP PBCH and PDCCH channel is

+    # D^24 + D^23 + D^21 + D^20 + D^17 + D^15 + D^13 + D^12 + D^8 + D^4 + D^2 + D + 1

+    crc_polynomial_pattern = [1,1,0,1,1,0,0,1,0,1,0,1,1,0,0,0,1,0,0,0,1,0,1,1,1]

+

+    # The CRC has P bits. P-min(P2,log2(L)) of these are used for error

+    # detection, where L is the list size. Meanwhile, min(P2,log2(L)) of

+    # them are used to improve error correction. So the CRC needs to be

+    # min(P2,log2(L)) number of bits longer than CRCs used in other codes,

+    # in order to achieve the same error detection capability.

+    P = len(crc_polynomial_pattern) - 1

+    P2 = 3

+    

+    # Determine the number of information and CRC bits.

+    K = A + P

+    

+    # Determine the number of bits used at the input and output of the polar

+    # encoder kernal.

+    N = get_3GPP_N(K, E, 9)   

+

+    # Get the 3GPP CRC interleaver pattern.

+    crc_interleaver_pattern = get_3GPP_crc_interleaver_pattern(K)

+    

+    # Get the 3GPP rate matching pattern.

+    rate_matching_pattern,mode = get_3GPP_rate_matching_pattern(K, N, E)

+

+    # Get the 3GPP sequence pattern.  

+    Q_N = get_3GPP_sequence_pattern(N)

+    

+    # Get the 3GPP information bit pattern.

+    info_bit_pattern = get_3GPP_info_bit_pattern(K, Q_N, rate_matching_pattern, mode)

+    

+    # Initialize scrambler

+    scrambler = Scrambler(NidCell, Lmax)

+    f_tilde_scrambled = scrambler.get_3GPP_2nd_scramble_s_seq(f_tilde, ibar_SSB) # scramble 864 LLRs 

+    

+    # Perform Distributed-CRC-and-Known-bit-Aided polar decoding.     

+    a_hat = DCKA_polar_decoder(f_tilde_scrambled, crc_polynomial_pattern, crc_interleaver_pattern, info_bit_pattern, rate_matching_pattern, mode, list_size, min_sum, P2)  

+    

+    if (a_hat == 0):

+        print "no MIB"

+    else:

+        cellSfn = 1     # System frame number for cell

+        scrambled = scrambler.get_3GPP_1st_scramble_s_seq(cellSfn, a_hat)   # Get scrambled hard coded bits

+        out = get_3GPP_PBCH_payload_intlv(scrambled)    # Get interleaved payload

+        

+        MIB_Parser(out, Lmax) # Parse MIB field messages from bit sequence

+        

diff --git a/mcu/tools/IQ_Analyzer/src/get_3GPP_N.py b/mcu/tools/IQ_Analyzer/src/get_3GPP_N.py
new file mode 100644
index 0000000..1f0d852
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/get_3GPP_N.py
@@ -0,0 +1,36 @@
+from __future__ import division

+import numpy as np

+

+#######################################################################################

+# GET_3GPP_N Obtain the number of bits in the input and output of the polar encoder 

+# kernal, according to Section 5.3.1 of 3GPP TS 38.212.

+# NOTE: N/A

+# input: 

+#        K should be an integer scalar. It specifies the number of bits in the

+#        information and CRC bit sequence.    

+#        E should be an integer scalar. It specifies the number of bits in the

+#        encoded bit sequence.      

+#        n_max should be an integer scalar. It specifies the log2 of the maximum

+#        number of bits in the input and output of the polar encoder kernal.

+#        n_max = 9 in the PBCH and PDCCH channels, while n_max = 10 in the PUCCH

+#        channel.

+# output: 

+#         N is the polar encoder kernal

+#######################################################################################

+def get_3GPP_N(K, E, n_max):

+

+    # 38.212 Polar coding 5.3.1

+    if (E <= np.dot((9 / 8),2 ** (np.ceil(np.log2(E)) - 1))) and (K / E < 9 / 16):

+        n_1 = np.ceil(np.log2(E)) - 1

+    else:

+        n_1 = np.ceil(np.log2(E))

+    

+    R_min = 1/8

+    n_min = 5

+

+    n_2 = np.ceil(np.log2(K / R_min))

+    n = max(n_min, min([n_1,n_2,n_max]))

+    N = np.power(2, n)

+    N = int(N)       

+

+    return N      
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/get_3GPP_crc_interleaver_pattern.py b/mcu/tools/IQ_Analyzer/src/get_3GPP_crc_interleaver_pattern.py
new file mode 100644
index 0000000..79d9474
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/get_3GPP_crc_interleaver_pattern.py
@@ -0,0 +1,48 @@
+import numpy as np 

+

+#################################################################################

+# GET_3GPP_CRC_INTERLEAVER_PATTERN Obtain the 3GPP Cyclic Redundancy Check 

+# (CRC) interleaver pattern, according to Section 5.3.1.1 of 3GPP TS 38.212 

+# the CRC interleaver pattern Pi is obtained.

+# NOTE: N/A

+# input: 

+#        K should be an integer scalar. It specifies the number of bits in 

+#        the information and CRC bit sequence.

+# output: 

+#         Pi will be an integer row vector, comprising K unique elements in the

+#         range 1 to K. Each integer identifies which one of the K information or 

+#         CRC bits provides the corresponding interleaved bit. Interleaving

+#         can be implemented according to c = b(Pi), while

+#         deinterleaving is implemented accoring to 

+#         b_hat(Pi) = c_hat.

+################################################################################# 

+def get_3GPP_crc_interleaver_pattern(K):

+           

+    # TS38.212 defines a 164-bit interleaver 

+    Pi_IL_max = [0,2,4,7,9,14,19,20,24,25,26,28,31,34,42,45,49,50,51,53,54,56,58,59,61,62,65,66,

+               67,69,70,71,72,76,77,81,82,83,87,88,89,91,93,95,98,101,104,106,108,110,111,113,

+               115,118,119,120,122,123,126,127,129,132,134,138,139,140,1,3,5,8,10,15,21,27,29,

+               32,35,43,46,52,55,57,60,63,68,73,78,84,90,92,94,96,99,102,105,107,109,112,114,

+               116,121,124,128,130,133,135,141,6,11,16,22,30,33,36,44,47,64,74,79,85,97,100,

+               103,117,125,131,136,142,12,17,23,37,48,75,80,86,137,143,13,18,38,144,39,145,40,

+               146,41,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163]

+    

+    if K > len(Pi_IL_max):

+        raise Exception('polar_3gpp_matlab:UnsupportedBlockLength, K should be no greater than 224.')

+    

+    # Interleaving 5.3.1.1

+    Pi_temp = np.zeros((K,1), dtype=int)

+

+    k = 0

+    K_IL_max = 164

+    Pi_IL_max = np.asarray(Pi_IL_max)

+    for m in xrange(K_IL_max):

+        if Pi_IL_max[m] >= (K_IL_max-K):

+            Pi_temp[k] = Pi_IL_max[m] - (K_IL_max - K)

+            k = k + 1

+    

+    # dummy manipulation for Pi array     

+    Pi = [0]*len(Pi_temp)

+    for i in xrange(len(Pi_temp)):

+        Pi[i] = int(Pi_temp[i])

+    return Pi
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/get_3GPP_info_bit_pattern.py b/mcu/tools/IQ_Analyzer/src/get_3GPP_info_bit_pattern.py
new file mode 100644
index 0000000..62f49d5
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/get_3GPP_info_bit_pattern.py
@@ -0,0 +1,115 @@
+import IQ_Utils

+import numpy as np

+

+###################################################################################

+# GET_3GPP_INFO_BIT_PATTERN Obtain the 3GPP information bit pattern, according to 

+# Section 5.3.1.2 of 3GPP TS 38.212

+# NOTE: N/A

+# input: 

+#        I should be an integer scalar. It specifies the number of bits in the 

+#        information, CRC and PC bit sequence. It should be no greater than N or E.       

+#        Q_N should be a row vector comprising N number of unique integers in the 

+#        range 1 to N. Each successive element of Q_N provides the index of the

+#        next most reliable input to the polar encoder kernal, where the first

+#        element of Q_N gives the index of the least reliable bit and the last

+#        element gives the index of the most reliable bit.       

+#        rate_matching_pattern should be a row vector comprising E number of

+#        integers, each having a value in the range 1 to N. Each integer

+#        identifies which one of the N outputs from the polar encoder kernal

+#        provides the corresponding bit in the encoded bit sequence e.      

+#        mode should have the value 'repetition', 'puncturing' or 'shortening'.

+#        This specifies how the rate matching has been achieved. 'repetition'

+#        indicates that some outputs of the polar encoder kernal are repeated in 

+#        the encoded bit sequence two or more times. 'puncturing' and 

+#        'shortening' indicate that some outputs of the polar encoder kernal 

+#        have been excluded from the encoded bit sequence. In the case of

+#        'puncturing' these excluded bits could have values of 0 or 1. In the

+#        case of 'shortening' these excluded bits are guaranteed to have values

+#        of 0.

+# output:     

+#         info_bit_pattern will be a row vector comprising N number of logical 

+#         elements, each having the value true or false. The number of elements 

+#         in info_bit_pattern having the value true will be I. These elements 

+#         having the value true identify the positions of the information and 

+#         CRC bits within the input to the polar encoder kernal. The

+#         information bit arrangement can be achieved according to

+#         u(info_bit_pattern) = a.

+###################################################################################  

+def get_3GPP_info_bit_pattern(I, Q_N, rate_matching_pattern, mode):

+

+    N = len(Q_N)

+    n = np.log2(N)

+    E = len(rate_matching_pattern)

+

+    if n != round(n):

+        raise Exception('N should be a power of 2')

+    

+    if I > N:

+        raise Exception('polar_3gpp_matlab:UnsupportedBlockLength, I should be no greater than N.')

+    

+    if I > E:

+        raise Exception('polar_3gpp_matlab:UnsupportedBlockLength, I should be no greater than E.')

+    

+    if max(rate_matching_pattern) > N:

+        raise Exception('rate_matching_pattern is not compatible with N')

+    

+    if 'repetition' in mode:

+        if E < N:

+            raise Exception('mode is not compatible with E')

+    else:

+        if 'puncturing' in mode:

+            if E >= N:

+                raise Exception('mode is not compatible with E')

+        else:

+            if 'shortening' in mode:

+                if E >= N:

+                    raise Exception('mode is not compatible with E')

+            else:

+                raise Exception('Unsupported mode')

+

+    # This is how the rate matching is described in TS 38.212

+#    P=[0,1,2,4,3,5,6,7,8,16,9,17,10,18,11,19,12,20,13,21,14,22,15,23,24,25,26,28,27,29,30,31]

+#    J=zeros(1,N)

+#    for n in range(0, N):

+#        i=floor(dot(32,n) / N)

+#        J[n]=dot(P[i],(N / 32)) + mod(n,N / 32)

+#        

+#        

+#    Q_Ftmp_N=[]

+#    if E < N:   # 5.4.1.1

+#        if I / E <= 7 / 16:

+#            for n in range(0, N-E-1):

+#                Q_Ftmp_N=[Q_Ftmp_N,J[n + 1]]

+#            if E >= dot(3,N) / 4:

+#                Q_Ftmp_N=[Q_Ftmp_N,arange(0,ceil(dot(3,N) / 4 - E / 2) - 1)]

+#            else:

+#                Q_Ftmp_N=[Q_Ftmp_N,arange(0,ceil(dot(9,N) / 16 - E / 4) - 1)]

+#        else:

+#            for n in range(E,N-1):

+#                Q_Ftmp_N=[Q_Ftmp_N,J[n + 1]]

+#        

+

+    

+    # This is an equivalent but more flexible version, which also works with any rate matching pattern

+    Q_Ftmp_N = np.setdiff1d(range(0,N),rate_matching_pattern)

+    

+    if 'puncturing' in mode:

+        raise Exception('Error: puncturing not supported')

+#        if E >= dot(3,N) / 4:

+#            Q_Ftmp_N=concat([Q_Ftmp_N,arange(0,ceil(dot(3,N)/4 - E/2) - 1)])

+#        else:

+#            Q_Ftmp_N=concat([Q_Ftmp_N,arange(0,ceil(dot(9,N)/16 - E/4) - 1)])

+

+

+    Q_Itmp_N = IQ_Utils.diff_stable(Q_N,Q_Ftmp_N)

+   

+    if len(Q_Itmp_N) < I:

+        raise Exception('polar_3gpp_matlab:UnsupportedBlockLength','Too many pre-frozen bits.')  

+

+    Q_I_N = Q_Itmp_N[-I:]

+

+    info_bit_pattern = [0]*N

+    for i in xrange(len(Q_I_N)):

+        info_bit_pattern[Q_I_N[i]] = 1

+

+    return info_bit_pattern

diff --git a/mcu/tools/IQ_Analyzer/src/get_3GPP_rate_matching_pattern.py b/mcu/tools/IQ_Analyzer/src/get_3GPP_rate_matching_pattern.py
new file mode 100644
index 0000000..602d21f
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/get_3GPP_rate_matching_pattern.py
@@ -0,0 +1,69 @@
+import numpy as np

+

+#######################################################################################

+# GET_3GPP_RATE_MATCHING_PATTERN Get the 3GPP rate matching sequence, as specified 

+# in Sections 5.4.1.1 and 5.4.1.2 of TS 38.212.

+# NOTE: N/A

+# input: 

+#        K should be an integer scalar. It specifies the number of bits in the 

+#        information and CRC bit sequence.        

+#        N should be an integer scalar, which should be a power of 2 and no less 

+#        than 32. It specifies the number of bits in the input and output of the 

+#        polar encoder kernal.         

+#        E should be an integer scalar. It specifies the number of bits in the 

+#        encoded bit sequence.        

+#        rate_matching_pattern will be a row vector comprising E number of

+#        integers, each having a value in the range 1 to N. Each integer

+#        identifies which one of the N outputs from the polar encoder kernal

+#        provides the corresponding bit in the encoded bit sequence. Rate

+#        matching can be achieved according to e = d(rate_matching_pattern).

+# output:        

+#         mode will have the value 'repetition', 'puncturing' or 'shortening'.

+#         This specifies how the rate matching has been achieved. 'repetition'

+#         indicates that some outputs of the polar encoder kernal are repeated in 

+#         the encoded bit sequence two or more times. 'puncturing' and 

+#         'shortening' indicate that some outputs of the polar encoder kernal 

+#         have been excluded from the encoded bit sequence. In the case of

+#         'puncturing' these excluded bits could have values of 0 or 1. In the

+#         case of 'shortening' these excluded bits are guaranteed to have values

+#         of 0.

+#######################################################################################  

+def get_3GPP_rate_matching_pattern(K, N, E):

+

+    n = np.log2(N)

+    N = int(N)

+    if n != round(n):

+        raise Exception('N should be a power of 2')

+    

+    if n < 5:

+        raise Exception('polar_3gpp_matlab:UnsupportedBlockLength, N should be no smaller than 32')

+    

+    # 38.213 Table 5.4.1.1.-1 sub-block interleaver pattern

+    P = [0,1,2,4,3,5,6,7,8,16,9,17,10,18,11,19,12,20,13,21,14,22,15,23,24,25,26,28,27,29,30,31]

+

+    d = range(0,N)      

+    # Sub-block interleaving

+    J = [0]*N

+    y = [0]*N      

+    for n in xrange(N):

+        i = int(np.floor(np.dot(32, n)/N))

+        J[n] = np.dot(P[i], (N/32)) + np.mod(n, N/32)

+        y[n] = d[J[n]]

+    

+    # Bit selection 5.4.1.2.

+    rate_matching_pattern = [0]*E   

+    if E >= N:

+        for k in xrange(E):

+            rate_matching_pattern[k] = y[np.mod(k, N)]

+        mode = 'repetition'

+    else:

+        if K / E <= 7 / 16:

+            for k in xrange(E):

+                rate_matching_pattern[k] = y[k + N - E]

+            mode = 'puncturing'

+        else:

+            for k in xrange(E):

+                rate_matching_pattern[k] = y[k]

+            mode = 'shortening'

+    

+    return rate_matching_pattern, mode
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/get_3GPP_sequence_pattern.py b/mcu/tools/IQ_Analyzer/src/get_3GPP_sequence_pattern.py
new file mode 100644
index 0000000..62f1100
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/get_3GPP_sequence_pattern.py
@@ -0,0 +1,78 @@
+import numpy  as np

+

+###################################################################################

+# GET_3GPP_SEQUENCE_PATTERN Get the 3GPP sequence, as specified in Table 

+# 5.3.1.2-1 of TS 38.212.

+# NOTE: N/A

+# input: 

+#        N should be an integer scalar, which should be a power of 2. It 

+#        specifies the number of bits in the input and output of the polar 

+#        encoder kernal.

+# output:     

+#         Q_N will be a row vector comprising N number of unique integers in the 

+#         range 1 to N. Each successive element of Q_N provides the index of the

+#         next most reliable input to the polar encoder kernal, where the first

+#         element of Q_N gives the index of the least reliable bit and the last

+#         element gives the index of the most reliable bit.

+###################################################################################  

+def get_3GPP_sequence_pattern(N): 

+    

+    if np.log2(N) != round(np.log2(N)):

+        raise Exception('N should be a power of 2')

+    

+    # 5.3.1 Polar sequence

+    Q_Nmax = [0,1,2,4,8,16,32,3,5,64,9,6,17,10,18,128,12,33,65,20,256,34,24,36,7,129,66,512,

+            11,40,68,130,19,13,48,14,72,257,21,132,35,258,26,513,80,37,25,22,136,260,264,

+            38,514,96,67,41,144,28,69,42,516,49,74,272,160,520,288,528,192,544,70,44,131,81,

+            50,73,15,320,133,52,23,134,384,76,137,82,56,27,97,39,259,84,138,145,261,29,43,98,

+            515,88,140,30,146,71,262,265,161,576,45,100,640,51,148,46,75,266,273,517,104,162,

+            53,193,152,77,164,768,268,274,518,54,83,57,521,112,135,78,289,194,85,276,522,58,

+            168,139,99,86,60,280,89,290,529,524,196,141,101,147,176,142,530,321,31,200,90,545,

+            292,322,532,263,149,102,105,304,296,163,92,47,267,385,546,324,208,386,150,153,165,

+            106,55,328,536,577,548,113,154,79,269,108,578,224,166,519,552,195,270,641,523,275,

+            580,291,59,169,560,114,277,156,87,197,116,170,61,531,525,642,281,278,526,177,293,388,

+            91,584,769,198,172,120,201,336,62,282,143,103,178,294,93,644,202,592,323,392,297,770,

+            107,180,151,209,284,648,94,204,298,400,608,352,325,533,155,210,305,547,300,109,184,534,

+            537,115,167,225,326,306,772,157,656,329,110,117,212,171,776,330,226,549,538,387,308,216,

+            416,271,279,158,337,550,672,118,332,579,540,389,173,121,553,199,784,179,228,338,312,704,

+            390,174,554,581,393,283,122,448,353,561,203,63,340,394,527,582,556,181,295,285,232,124,

+            205,182,643,562,286,585,299,354,211,401,185,396,344,586,645,593,535,240,206,95,327,564,800,

+            402,356,307,301,417,213,568,832,588,186,646,404,227,896,594,418,302,649,771,360,539,111,331,

+            214,309,188,449,217,408,609,596,551,650,229,159,420,310,541,773,610,657,333,119,600,339,218,

+            368,652,230,391,313,450,542,334,233,555,774,175,123,658,612,341,777,220,314,424,395,673,583,

+            355,287,183,234,125,557,660,616,342,316,241,778,563,345,452,397,403,207,674,558,785,432,357,

+            187,236,664,624,587,780,705,126,242,565,398,346,456,358,405,303,569,244,595,189,566,676,361,

+            706,589,215,786,647,348,419,406,464,680,801,362,590,409,570,788,597,572,219,311,708,598,601,

+            651,421,792,802,611,602,410,231,688,653,248,369,190,364,654,659,335,480,315,221,370,613,422,

+            425,451,614,543,235,412,343,372,775,317,222,426,453,237,559,833,804,712,834,661,808,779,617,

+            604,433,720,816,836,347,897,243,662,454,318,675,618,898,781,376,428,665,736,567,840,625,238,

+            359,457,399,787,591,678,434,677,349,245,458,666,620,363,127,191,782,407,436,626,571,465,681,

+            246,707,350,599,668,790,460,249,682,573,411,803,789,709,365,440,628,689,374,423,466,793,250,

+            371,481,574,413,603,366,468,655,900,805,615,684,710,429,794,252,373,605,848,690,713,632,482,

+            806,427,904,414,223,663,692,835,619,472,455,796,809,714,721,837,716,864,810,606,912,722,696,

+            377,435,817,319,621,812,484,430,838,667,488,239,378,459,622,627,437,380,818,461,496,669,679,

+            724,841,629,351,467,438,737,251,462,442,441,469,247,683,842,738,899,670,783,849,820,728,928,

+            791,367,901,630,685,844,633,711,253,691,824,902,686,740,850,375,444,470,483,415,485,905,795,

+            473,634,744,852,960,865,693,797,906,715,807,474,636,694,254,717,575,913,798,811,379,697,431,

+            607,489,866,723,486,908,718,813,476,856,839,725,698,914,752,868,819,814,439,929,490,623,671,

+            739,916,463,843,381,497,930,821,726,961,872,492,631,729,700,443,741,845,920,382,822,851,730,

+            498,880,742,445,471,635,932,687,903,825,500,846,745,826,732,446,962,936,475,853,867,637,907,

+            487,695,746,828,753,854,857,504,799,255,964,909,719,477,915,638,748,944,869,491,699,754,858,

+            478,968,383,910,815,976,870,917,727,493,873,701,931,756,860,499,731,823,922,874,918,502,933,

+            743,760,881,494,702,921,501,876,847,992,447,733,827,934,882,937,963,747,505,855,924,734,829,

+            965,938,884,506,749,945,966,755,859,940,830,911,871,639,888,479,946,750,969,508,861,757,970,

+            919,875,862,758,948,977,923,972,761,877,952,495,703,935,978,883,762,503,925,878,735,993,885,

+            939,994,980,926,764,941,967,886,831,947,507,889,984,751,942,996,971,890,509,949,973,1000,892,

+            950,863,759,1008,510,979,953,763,974,954,879,981,982,927,995,765,956,887,985,997,986,943,891,

+            998,766,511,988,1001,951,1002,893,975,894,1009,955,1004,1010,957,983,958,987,1012,999,1016,767,

+            989,1003,990,1005,959,1011,1013,895,1006,1014,1017,1018,991,1020,1007,1015,1019,1021,1022,1023]

+    

+    if N > len(Q_Nmax):

+        raise Exception('Value of N is unsupported')

+    

+    Q_N = []

+    for i in xrange(len(Q_Nmax)):

+        if (Q_Nmax[i] < N):

+            Q_N.append(Q_Nmax[i])

+    

+    return Q_N
\ No newline at end of file
diff --git a/mcu/tools/IQ_Analyzer/src/get_crc_generator_matrix.py b/mcu/tools/IQ_Analyzer/src/get_crc_generator_matrix.py
new file mode 100644
index 0000000..e287017
--- /dev/null
+++ b/mcu/tools/IQ_Analyzer/src/get_crc_generator_matrix.py
@@ -0,0 +1,37 @@
+import IQ_Utils

+import numpy as np

+

+##################################################################################

+# GET_CRC_GENERATOR_MATRIX Obtain a Cyclic Redudancy Check (CRC) generator 

+# matrix.

+# NOTE: N/A

+# input: A should be an integer scalar. It specifies the number of bits in the

+#        information bit sequence.

+#        crc_polynomial_pattern should be a binary row vector comprising P+1

+#        number of bits, each having the value 0 or 1. These bits parameterise a

+#        Cyclic Redundancy Check (CRC) comprising P bits. Each bit provides the

+#        coefficient of the corresponding element in the CRC generator

+#        polynomial. From left to right, the bits provide the coefficients for

+#        the elements D^P, D^P-1, D^P-2, ..., D^2, D, 1.

+# output: G_P will be a K by P binary matrix. The CRC bits can be generated

+#         according to mod(a*G_P,2).

+##################################################################################  

+def get_crc_generator_matrix(A, crc_polynomial_pattern):

+    

+    P = len(crc_polynomial_pattern) - 1

+

+    if P < 1:

+        raise Exception('crc_polynomial_pattern is invalid')

+    

+    G_P = np.zeros((A,P), dtype=int)

+

+    if A > 0:

+        G_P[31, :] = crc_polynomial_pattern[1:]

+        for k in xrange(A-1, 0, -1):

+            a = G_P[k,1:]

+            a = np.append(a,0)

+            b = np.multiply(G_P[k,0],crc_polynomial_pattern[1:])

+            xor_result = IQ_Utils.xor(a, b)

+            G_P[k-1,:] = xor_result

+            

+    return G_P