def generate_symbols_for_eq(method, M, dtype): #TODO: investigate if it makes sense to include the calculations of constants inside the methods if method in ["cma"]: return np.atleast_2d(_cal_Rconstant(M) + 0j).astype(dtype) if method in ["sgncma"]: return np.atleast_2d(_cal_Rconstant(M) + 0j).astype(dtype) if method in ["mcma"]: return np.atleast_2d(_cal_Rconstant_complex(M)).astype(dtype) if method in ["rde"]: p = generate_partition_codes_radius(M) return np.atleast_2d(p+0j).astype(dtype) if method in ["mrde"]: p = generate_partition_codes_complex(M) return np.atleast_2d(p).astype(dtype) if method in ["sbd"]: symbols = np.atleast_2d(cal_symbols_qam(M)/np.sqrt(cal_scaling_factor_qam(M))).astype(dtype) return symbols if method in ["mddma"]: symbols = np.atleast_2d(cal_symbols_qam(M)/np.sqrt(cal_scaling_factor_qam(M))).astype(dtype) return symbols if method in ["dd"]: symbols = np.atleast_2d(cal_symbols_qam(M)/np.sqrt(cal_scaling_factor_qam(M))).astype(dtype) return symbols if method in ["sgncma_real"]: return np.repeat([np.atleast_1d(_cal_Rconstant_complex(M).real.astype(dtype))], 2, axis=0) if method in ["cma_real"]: return np.repeat([np.atleast_1d(_cal_Rconstant_complex(M).real.astype(dtype))], 2, axis=0) if method in ["dd_real"]: symbols = cal_symbols_qam(M)/np.sqrt(cal_scaling_factor_qam(M)) symbols_out = np.vstack([symbols.real, symbols.imag]).astype(dtype) return symbols_out if method in ["dd_data_real", "sbd_data"]: raise ValueError("%s is a data-aided method and needs the symbols to be passed"%method) raise ValueError("%s is unknown method"%method)
def _select_errorfct(method, M, symbols, dtype, **kwargs): #TODO: investigate if it makes sense to include the calculations of constants inside the methods if method in ["mcma"]: return ErrorFctMCMA(_cal_Rconstant_complex(M)) elif method in ["cma"]: return ErrorFctCMA(_cal_Rconstant(M)) elif method in ["rde"]: p, c = generate_partition_codes_radius(M) return ErrorFctRDE(p, c) elif method in ["mrde"]: p, c = generate_partition_codes_complex(M) return ErrorFctMRDE(p, c) elif method in ["sca"]: return ErrorFctSCA(_cal_Rsca(M)) elif method in ["cme"]: if symbols is None: syms = cal_symbols_qam(M) / np.sqrt(cal_scaling_factor_qam(M)) syms = syms.astype(dtype) else: syms = symbols d = np.min( abs(np.diff(np.unique(syms.real))) ) # should be fixed to consider different spacing between real and imag R = _cal_Rconstant(M) r2 = np.mean(abs(syms)**2) r4 = np.mean(abs(syms)**4) A = r4 / r2 bb = np.max(abs(syms * abs(syms)**2 - A)) try: beta = kwargs['beta'] except: r2 = np.mean(abs(syms)**2) r4 = np.mean(abs(syms)**4) A = r4 / r2 beta = np.max(abs(syms * abs(syms)**2 - A)) / 2 return ErrorFctCME(R, d, beta) elif method in ['sbd']: if symbols is None: return ErrorFctSBD( (cal_symbols_qam(M) / np.sqrt(cal_scaling_factor_qam(M))).astype(dtype)) else: return ErrorFctSBD(symbols) elif method in ['mddma']: return ErrorFctMDDMA( (cal_symbols_qam(M) / np.sqrt(cal_scaling_factor_qam(M))).astype(dtype)) elif method in ['dd']: return ErrorFctDD((cal_symbols_qam(M) / np.sqrt(cal_scaling_factor_qam(M))).astype(dtype)) else: raise ValueError("%s is unknown method" % method)
def generate_partition_codes_complex(M): """ Generate complex partitions and codes for M-QAM for MRDE based on the real and imaginary radii of the different symbols. The partitions define the boundaries between the different codes. This is used to determine on which real/imaginary radius a signal symbol should lie on. The real and imaginary parts should be used for parititioning the real and imaginary parts of the signal in MRDE. Parameters ---------- M : int M-QAM order Returns ------- parts : array_like the boundaries between the different codes for parititioning codes : array_like the nearest symbol radius """ syms = cal_symbols_qam(M) scale = cal_scaling_factor_qam(M) syms /= np.sqrt(scale) syms_r = np.unique(abs(syms.real)**4 / abs(syms.real)**2) syms_i = np.unique(abs(syms.imag)**4 / abs(syms.imag)**2) codes = syms_r + 1.j * syms_i part_r = syms_r[:-1] + np.diff(syms_r) / 2 part_i = syms_i[:-1] + np.diff(syms_i) / 2 parts = part_r + 1.j * part_i return np.hstack([parts, codes])
def _cal_Rsca(M): syms = cal_symbols_qam(M) syms /= np.sqrt(cal_scaling_factor_qam(M)) Rd = _cal_Rdash(syms) return np.mean( (abs(syms.real + syms.imag) + abs(syms.real - syms.imag))**2 * Rd) / (4 * np.mean(Rd))
def test_symbols(self, M): s = signals.SignalQAMGrayCoded(M, 1000, nmodes=1) si = np.unique(s) thsyms = theory.cal_symbols_qam(M) / np.sqrt(theory.cal_scaling_factor_qam(M)) d = np.min(abs(s[0, :, np.newaxis] - thsyms), axis=1) assert si.shape[0] == M npt.assert_array_almost_equal(d, 0)
def test_reshape_symbols_DD_cmplx(self, method, M, nmodes, pass_syms): from qampy import theory from qampy.core.equalisation.equalisation import _reshape_symbols syms = theory.cal_symbols_qam(M)/np.sqrt(theory.cal_scaling_factor_qam(M)) if pass_syms: s = _reshape_symbols(syms, method, M, np.complex128, nmodes) else: s = _reshape_symbols(None, method, M, np.complex128, nmodes) assert s.shape[0] == nmodes assert s.shape[1] == syms.shape[0] for i in range(nmodes): npt.assert_allclose(s[i], syms)
def test_reshape_symbols_DD_real(self, M, nmodes, pass_syms): from qampy import theory from qampy.core.equalisation.equalisation import _reshape_symbols syms = theory.cal_symbols_qam(M)/np.sqrt(theory.cal_scaling_factor_qam(M)) if pass_syms is None: s = _reshape_symbols(None, "dd_real", M, np.float64, nmodes*2) elif pass_syms is "cmplx": s = _reshape_symbols(syms, "dd_real", M, np.float64, nmodes*2) elif pass_syms is "float": s = _reshape_symbols(np.array([syms.real, syms.imag]), "dd_real", M, np.float64, nmodes*2) assert s.shape[0] == nmodes*2 assert s.shape[1] == syms.shape[0] for i in range(nmodes*2): if i < nmodes: npt.assert_allclose(s[i], syms.real) else: npt.assert_allclose(s[i], syms.imag)
def generate_partition_codes_radius(M): """ Generate partitions and codes for M-QAM for RDE based on the radius of the different symbols. The partitions define the boundaries between the different codes. This is used to determine on which radius a signal symbol should lie. Parameters ---------- M : int M-QAM order Returns ------- parts : array_like the boundaries between the different codes for parititioning codes : array_like the nearest symbol radius """ syms = cal_symbols_qam(M) scale = cal_scaling_factor_qam(M) syms /= np.sqrt(scale) codes = np.unique(abs(syms)**4 / abs(syms)**2) parts = codes[:-1] + np.diff(codes) / 2 return np.hstack([parts, codes])
def _cal_gamma(M): """Calculate the gamma factor for SNR estimation.""" A = abs(cal_symbols_qam(M)) / np.sqrt(cal_scaling_factor_qam(M)) uniq, counts = np.unique(A, return_counts=True) return np.sum(uniq**4 * counts / M)
def _cal_Rconstant_complex(M): syms = cal_symbols_qam(M) scale = cal_scaling_factor_qam(M) syms /= np.sqrt(scale) return np.mean(syms.real**4) / np.mean(syms.real**2) + 1.j * np.mean( syms.imag**4) / np.mean(syms.imag**2)
def _cal_Rconstant(M): syms = cal_symbols_qam(M) scale = cal_scaling_factor_qam(M) syms /= np.sqrt(scale) return np.mean(abs(syms)**4) / np.mean(abs(syms)**2)