[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