x = np.zeros(len(t))<EOL>for k,tt in enumerate(t):<EOL><INDENT>if tt >= <NUM_LIT:0>:<EOL><INDENT>x[k] = <NUM_LIT:1.0><EOL><DEDENT><DEDENT>return x<EOL>

Approximation to step function signal u(t).

In this numerical version of u(t) the step turns on at t = 0.

Parameters
----------
t : ndarray of the time axis

Returns
-------
x : ndarray of the step function signal u(t)

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.sigsys import step
>>> t = arange(-1,5,.01)
>>> x = step(t)
>>> plt.plot(t,x)
>>> plt.ylim([-0.01, 1.01])
>>> plt.show()

To turn on at t = 1, shift t.

>>> x = step(t - 1.0)
>>> plt.ylim([-0.01, 1.01])
>>> plt.plot(t,x)


x = np.zeros(len(t))<EOL>for k,tk in enumerate(t):<EOL><INDENT>if np.abs(tk) > tau/<NUM_LIT>:<EOL><INDENT>x[k] = <NUM_LIT:0><EOL><DEDENT>else:<EOL><INDENT>x[k] = <NUM_LIT:1><EOL><DEDENT><DEDENT>return x<EOL>

Approximation to the rectangle pulse Pi(t/tau).

In this numerical version of Pi(t/tau) the pulse is active
over -tau/2 <= t <= tau/2.

Parameters
----------
t : ndarray of the time axis
tau : the pulse width

Returns
-------
x : ndarray of the signal Pi(t/tau)

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.sigsys import rect
>>> t = arange(-1,5,.01)
>>> x = rect(t,1.0)
>>> plt.plot(t,x)
>>> plt.ylim([0, 1.01])
>>> plt.show()

To turn on the pulse at t = 1 shift t.

>>> x = rect(t - 1.0,1.0)
>>> plt.plot(t,x)
>>> plt.ylim([0, 1.01])


x = np.zeros(len(t))<EOL>for k,tk in enumerate(t):<EOL><INDENT>if np.abs(tk) > tau/<NUM_LIT:1.>:<EOL><INDENT>x[k] = <NUM_LIT:0><EOL><DEDENT>else:<EOL><INDENT>x[k] = <NUM_LIT:1> - np.abs(tk)/tau<EOL><DEDENT><DEDENT>return x<EOL>

Approximation to the triangle pulse Lambda(t/tau).

In this numerical version of Lambda(t/tau) the pulse is active
over -tau <= t <= tau.

Parameters
----------
t : ndarray of the time axis
tau : one half the triangle base width

Returns
-------
x : ndarray of the signal Lambda(t/tau)

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.sigsys import tri
>>> t = arange(-1,5,.01)
>>> x = tri(t,1.0)
>>> plt.plot(t,x)
>>> plt.show()

To turn on at t = 1, shift t.

>>> x = tri(t - 1.0,1.0)
>>> plt.plot(t,x)


x = np.zeros(len(n))<EOL>for k,nn in enumerate(n):<EOL><INDENT>if nn == <NUM_LIT:0>:<EOL><INDENT>x[k] = <NUM_LIT:1.0><EOL><DEDENT><DEDENT>return x<EOL>

Discrete impulse function delta[n].

Parameters
----------
n : ndarray of the time axis

Returns
-------
x : ndarray of the signal delta[n]

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.sigsys import dimpulse
>>> n = arange(-5,5)
>>> x = dimpulse(n)
>>> plt.stem(n,x)
>>> plt.show()

Shift the delta left by 2.

>>> x = dimpulse(n+2)
>>> plt.stem(n,x)


x = np.zeros(len(n))<EOL>for k,nn in enumerate(n):<EOL><INDENT>if nn >= <NUM_LIT:0>:<EOL><INDENT>x[k] = <NUM_LIT:1.0><EOL><DEDENT><DEDENT>return x<EOL>

Discrete step function u[n].

Parameters
----------
n : ndarray of the time axis

Returns
-------
x : ndarray of the signal u[n]

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.sigsys import dstep
>>> n = arange(-5,5)
>>> x = dstep(n)
>>> plt.stem(n,x)
>>> plt.show()

Shift the delta left by 2.

>>> x = dstep(n+2)
>>> plt.stem(n,x)


x = np.zeros(len(n))<EOL>for k,nn in enumerate(n):<EOL><INDENT>if nn >= <NUM_LIT:0> and nn < N:<EOL><INDENT>x[k] = <NUM_LIT:1.0><EOL><DEDENT><DEDENT>return x<EOL>

Discrete rectangle function of duration N samples.

The signal is active on the interval 0 <= n <= N-1. Also known
as the rectangular window function, which is available in
scipy.signal.

Parameters
----------
n : ndarray of the time axis
N : the pulse duration

Returns
-------
x : ndarray of the signal

Notes
-----
The discrete rectangle turns on at n = 0, off at n = N-1 and
has duration of exactly N samples.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.sigsys import drect
>>> n = arange(-5,5)
>>> x = drect(n, N=3)
>>> plt.stem(n,x)
>>> plt.show()

Shift the delta left by 2.

>>> x = drect(n+2, N=3)
>>> plt.stem(n,x)


<EOL>n = np.arange(-M*Ns,M*Ns+<NUM_LIT:1>)<EOL>b = np.zeros(len(n));<EOL>a = alpha;<EOL>Ns *= <NUM_LIT:1.0><EOL>for i in range(len(n)):<EOL><INDENT>if (<NUM_LIT:1> - <NUM_LIT:4>*(a*n[i]/Ns)**<NUM_LIT:2>) == <NUM_LIT:0>:<EOL><INDENT>b[i] = np.pi/<NUM_LIT:4>*np.sinc(<NUM_LIT:1>/(<NUM_LIT>*a))<EOL><DEDENT>else:<EOL><INDENT>b[i] = np.sinc(n[i]/Ns)*np.cos(np.pi*a*n[i]/Ns)/(<NUM_LIT:1> - <NUM_LIT:4>*(a*n[i]/Ns)**<NUM_LIT:2>)<EOL><DEDENT><DEDENT>return b<EOL>

A truncated raised cosine pulse used in digital communications.

The pulse shaping factor :math:`0< \\alpha < 1` is required as well as the
truncation factor M which sets the pulse duration to be 2*M*Tsymbol.

Parameters
----------
Ns : number of samples per symbol
alpha : excess bandwidth factor on (0, 1), e.g., 0.35
M : equals RC one-sided symbol truncation factor

Returns
-------
b : ndarray containing the pulse shape

Notes
-----
The pulse shape b is typically used as the FIR filter coefficients
when forming a pulse shaped digital communications waveform.

Examples
--------
Ten samples per symbol and alpha = 0.35.

>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.sigsys import rc_imp
>>> b = rc_imp(10,0.35)
>>> n = arange(-10*6,10*6+1)
>>> plt.stem(n,b)
>>> plt.show()


<EOL>n = np.arange(-M*Ns,M*Ns+<NUM_LIT:1>)<EOL>b = np.zeros(len(n))<EOL>Ns *= <NUM_LIT:1.0><EOL>a = alpha<EOL>for i in range(len(n)):<EOL><INDENT>if abs(<NUM_LIT:1> - <NUM_LIT:16>*a**<NUM_LIT:2>*(n[i]/Ns)**<NUM_LIT:2>) <= np.finfo(np.float).eps/<NUM_LIT:2>:<EOL><INDENT>b[i] = <NUM_LIT:1>/<NUM_LIT>*((<NUM_LIT:1>+a)*np.sin((<NUM_LIT:1>+a)*np.pi/(<NUM_LIT>*a))-(<NUM_LIT:1>-a)*np.cos((<NUM_LIT:1>-a)*np.pi/(<NUM_LIT>*a))+(<NUM_LIT:4>*a)/np.pi*np.sin((<NUM_LIT:1>-a)*np.pi/(<NUM_LIT>*a)))<EOL><DEDENT>else:<EOL><INDENT>b[i] = <NUM_LIT:4>*a/(np.pi*(<NUM_LIT:1> - <NUM_LIT:16>*a**<NUM_LIT:2>*(n[i]/Ns)**<NUM_LIT:2>))<EOL>b[i] = b[i]*(np.cos((<NUM_LIT:1>+a)*np.pi*n[i]/Ns) + np.sinc((<NUM_LIT:1>-a)*n[i]/Ns)*(<NUM_LIT:1>-a)*np.pi/(<NUM_LIT>*a))<EOL><DEDENT><DEDENT>return b<EOL>

A truncated square root raised cosine pulse used in digital communications.

The pulse shaping factor 0< alpha < 1 is required as well as the
truncation factor M which sets the pulse duration to be 2*M*Tsymbol.

Parameters
----------
Ns : number of samples per symbol
alpha : excess bandwidth factor on (0, 1), e.g., 0.35
M : equals RC one-sided symbol truncation factor

Returns
-------
b : ndarray containing the pulse shape

Notes
-----
The pulse shape b is typically used as the FIR filter coefficients
when forming a pulse shaped digital communications waveform. When
square root raised cosine (SRC) pulse is used generate Tx signals and
at the receiver used as a matched filter (receiver FIR filter), the
received signal is now raised cosine shaped, this having zero
intersymbol interference and the optimum removal of additive white
noise if present at the receiver input.

Examples
--------
>>> # ten samples per symbol and alpha = 0.35
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.sigsys import sqrt_rc_imp
>>> b = sqrt_rc_imp(10,0.35)
>>> n = arange(-10*6,10*6+1)
>>> plt.stem(n,b)
>>> plt.show()


c = m_seq(m)<EOL>Q = len(c)<EOL>max_periods = int(np.ceil(N_bits/float(Q)))<EOL>PN = np.zeros(max_periods*Q)<EOL>for k in range(max_periods):<EOL><INDENT>PN[k*Q:(k+<NUM_LIT:1>)*Q] = c<EOL><DEDENT>PN = np.resize(PN, (<NUM_LIT:1>,N_bits))<EOL>return PN.flatten()<EOL>

Maximal length sequence signal generator.

Generates a sequence 0/1 bits of N_bit duration. The bits themselves
are obtained from an m-sequence of length m. Available m-sequence
(PN generators) include m = 2,3,...,12, & 16.

Parameters
----------
N_bits : the number of bits to generate
m : the number of shift registers. 2,3, .., 12, & 16

Returns
-------
PN : ndarray of the generator output over N_bits

Notes
-----
The sequence is periodic having period 2**m - 1 (2^m - 1).

Examples
--------
>>> # A 15 bit period signal nover 50 bits
>>> PN = PN_gen(50,4)


if m == <NUM_LIT:2>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:1>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:3>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:4>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:5>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:6>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:7>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:8>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:1>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:9>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:10>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:11>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:12>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:1>])<EOL><DEDENT>elif m == <NUM_LIT:16>:<EOL><INDENT>taps = np.array([<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:1>])<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>sr = np.ones(m)<EOL>Q = <NUM_LIT:2>**m - <NUM_LIT:1><EOL>c = np.zeros(Q)<EOL>for n in range(Q):<EOL><INDENT>tap_xor = <NUM_LIT:0><EOL>c[n] = sr[-<NUM_LIT:1>]<EOL>for k in range(<NUM_LIT:1>,m):<EOL><INDENT>if taps[k] == <NUM_LIT:1>:<EOL><INDENT>tap_xor = np.bitwise_xor(tap_xor,np.bitwise_xor(int(sr[-<NUM_LIT:1>]),int(sr[m-<NUM_LIT:1>-k])))<EOL><DEDENT><DEDENT>sr[<NUM_LIT:1>:] = sr[:-<NUM_LIT:1>]<EOL>sr[<NUM_LIT:0>] = tap_xor<EOL><DEDENT>return c<EOL>

Generate an m-sequence ndarray using an all-ones initialization.

Available m-sequence (PN generators) include m = 2,3,...,12, & 16.

Parameters
----------
m : the number of shift registers. 2,3, .., 12, & 16

Returns
-------
c : ndarray of one period of the m-sequence

Notes
-----
The sequence period is 2**m - 1 (2^m - 1).

Examples
--------
>>> c = m_seq(5)


def BPSK_tx(N_bits,Ns,ach_fc=<NUM_LIT>,ach_lvl_dB=-<NUM_LIT:100>,pulse='<STR_LIT>',alpha = <NUM_LIT>,M=<NUM_LIT:6>):

pulse_types = ['<STR_LIT>', '<STR_LIT:src>']<EOL>if pulse not in pulse_types:<EOL><INDENT>raise ValueError('<STR_LIT>''<STR_LIT>')<EOL><DEDENT>x0,b,data0 = NRZ_bits(N_bits,Ns,pulse,alpha,M)<EOL>x1p,b,data1p = NRZ_bits(N_bits,Ns,pulse,alpha,M)<EOL>x1m,b,data1m = NRZ_bits(N_bits,Ns,pulse,alpha,M)<EOL>n = np.arange(len(x0))<EOL>x1p = x1p*np.exp(<NUM_LIT>*<NUM_LIT:2>*np.pi*ach_fc/float(Ns)*n)<EOL>x1m = x1m*np.exp(-<NUM_LIT>*<NUM_LIT:2>*np.pi*ach_fc/float(Ns)*n)<EOL>ach_lvl = <NUM_LIT:10>**(ach_lvl_dB/<NUM_LIT>)<EOL>return x0 + ach_lvl*(x1p + x1m), b, data0<EOL>

Generates biphase shift keyed (BPSK) transmitter with adjacent channel interference.

Generates three BPSK signals with rectangular or square root raised cosine (SRC)
pulse shaping of duration N_bits and Ns samples per bit. The desired signal is
centered on f = 0, which the adjacent channel signals to the left and right
are also generated at dB level relative to the desired signal. Used in the
digital communications Case Study supplement.

Parameters
----------
N_bits : the number of bits to simulate
Ns : the number of samples per bit
ach_fc : the frequency offset of the adjacent channel signals (default 2.0)
ach_lvl_dB : the level of the adjacent channel signals in dB (default -100)
pulse :the pulse shape 'rect' or 'src'
alpha : square root raised cosine pulse shape factor (default = 0.25)
M : square root raised cosine pulse truncation factor (default = 6)

Returns
-------
x : ndarray of the composite signal x0 + ach_lvl*(x1p + x1m)
b : the transmit pulse shape
data0 : the data bits used to form the desired signal; used for error checking

Notes
-----

Examples
--------
>>> x,b,data0 = BPSK_tx(1000,10,pulse='src')


def NRZ_bits(N_bits,Ns,pulse='<STR_LIT>',alpha = <NUM_LIT>,M=<NUM_LIT:6>):

data = np.random.randint(<NUM_LIT:0>,<NUM_LIT:2>,N_bits)<EOL>n_zeros = np.zeros((N_bits,int(Ns)-<NUM_LIT:1>))<EOL>x = np.hstack((<NUM_LIT:2>*data.reshape(N_bits,<NUM_LIT:1>)-<NUM_LIT:1>,n_zeros))<EOL>x =x.flatten()<EOL>if pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = np.ones(int(Ns))<EOL><DEDENT>elif pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = rc_imp(Ns,alpha,M)<EOL><DEDENT>elif pulse.lower() == '<STR_LIT:src>':<EOL><INDENT>b = sqrt_rc_imp(Ns,alpha,M)<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>x = signal.lfilter(b,<NUM_LIT:1>,x)<EOL>return x,b/float(Ns),data<EOL>

Generate non-return-to-zero (NRZ) data bits with pulse shaping.

A baseband digital data signal using +/-1 amplitude signal values
and including pulse shaping.

Parameters
----------
N_bits : number of NRZ +/-1 data bits to produce
Ns : the number of samples per bit,
pulse_type : 'rect' , 'rc', 'src' (default 'rect')
alpha : excess bandwidth factor(default 0.25)
M : single sided pulse duration (default = 6)

Returns
-------
x : ndarray of the NRZ signal values
b : ndarray of the pulse shape
data : ndarray of the underlying data bits

Notes
-----
Pulse shapes include 'rect' (rectangular), 'rc' (raised cosine),
'src' (root raised cosine). The actual pulse length is 2*M+1 samples.
This function is used by BPSK_tx in the Case Study article.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm.sigsys import NRZ_bits
>>> from numpy import arange
>>> x,b,data = NRZ_bits(100, 10)
>>> t = arange(len(x))
>>> plt.plot(t, x)
>>> plt.ylim([-1.01, 1.01])
>>> plt.show()


def NRZ_bits2(data,Ns,pulse='<STR_LIT>',alpha = <NUM_LIT>,M=<NUM_LIT:6>):

N_bits = len(data)<EOL>n_zeros = np.zeros((N_bits,int(Ns)-<NUM_LIT:1>))<EOL>x = np.hstack((<NUM_LIT:2>*data.reshape(N_bits,<NUM_LIT:1>)-<NUM_LIT:1>,n_zeros))<EOL>x = x.flatten()<EOL>if pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = np.ones(int(Ns))<EOL><DEDENT>elif pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = rc_imp(Ns,alpha,M)<EOL><DEDENT>elif pulse.lower() == '<STR_LIT:src>':<EOL><INDENT>b = sqrt_rc_imp(Ns,alpha,M)<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>x = signal.lfilter(b,<NUM_LIT:1>,x)<EOL>return x,b/float(Ns)<EOL>

Generate non-return-to-zero (NRZ) data bits with pulse shaping with user data

A baseband digital data signal using +/-1 amplitude signal values
and including pulse shaping. The data sequence is user supplied.

Parameters
----------
data : ndarray of the data bits as 0/1 values
Ns : the number of samples per bit,
pulse_type : 'rect' , 'rc', 'src' (default 'rect')
alpha : excess bandwidth factor(default 0.25)
M : single sided pulse duration (default = 6)

Returns
-------
x : ndarray of the NRZ signal values
b : ndarray of the pulse shape

Notes
-----
Pulse shapes include 'rect' (rectangular), 'rc' (raised cosine),
'src' (root raised cosine). The actual pulse length is 2*M+1 samples.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm.sigsys import NRZ_bits2
>>> from sk_dsp_comm.sigsys import m_seq
>>> from numpy import arange
>>> x,b = NRZ_bits2(m_seq(5),10)
>>> t = arange(len(x))
>>> plt.ylim([-1.01, 1.01])
>>> plt.plot(t,x)


plt.figure(figsize=(<NUM_LIT:6>,<NUM_LIT:4>))<EOL>idx = np.arange(<NUM_LIT:0>,L+<NUM_LIT:1>)<EOL>plt.plot(idx,x[S:S+L+<NUM_LIT:1>],'<STR_LIT:b>')<EOL>k_max = int((len(x) - S)/L)-<NUM_LIT:1><EOL>for k in range(<NUM_LIT:1>,k_max):<EOL><INDENT>plt.plot(idx,x[S+k*L:S+L+<NUM_LIT:1>+k*L],'<STR_LIT:b>')<EOL><DEDENT>plt.grid()<EOL>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL>return <NUM_LIT:0><EOL>

Eye pattern plot of a baseband digital communications waveform.

The signal must be real, but can be multivalued in terms of the underlying
modulation scheme. Used for BPSK eye plots in the Case Study article.

Parameters
----------
x : ndarray of the real input data vector/array
L : display length in samples (usually two symbols)
S : start index

Returns
-------
Nothing : A plot window opens containing the eye plot

Notes
-----
Increase S to eliminate filter transients.

Examples
--------
1000 bits at 10 samples per bit with 'rc' shaping.

>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm import sigsys as ss
>>> x,b, data = ss.NRZ_bits(1000,10,'rc')
>>> ss.eye_plot(x,20,60)


xI = np.real(x[start::Ns])<EOL>xQ = np.imag(x[start::Ns])<EOL>return xI, xQ<EOL>

Sample a baseband digital communications waveform at the symbol spacing.

Parameters
----------
x : ndarray of the input digital comm signal
Ns : number of samples per symbol (bit)
start : the array index to start the sampling

Returns
-------
xI : ndarray of the real part of x following sampling
xQ : ndarray of the imaginary part of x following sampling

Notes
-----
Normally the signal is complex, so the scatter plot contains
clusters at points in the complex plane. For a binary signal
such as BPSK, the point centers are nominally +/-1 on the real
axis. Start is used to eliminate transients from the FIR
pulse shaping filters from appearing in the scatter plot.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm import sigsys as ss
>>> x,b, data = ss.NRZ_bits(1000,10,'rc')
>>> # Add some noise so points are now scattered about +/-1
>>> y = ss.cpx_AWGN(x,20,10)
>>> yI,yQ = ss.scatter(y,10,60)
>>> plt.plot(yI,yQ,'.')
>>> plt.axis('equal')
>>> plt.ylabel("Quadrature")
>>> plt.xlabel("In-Phase")
>>> plt.grid()
>>> plt.show()


Pe_hat = np.sum(data[<NUM_LIT:0>:len(z[start::Ns])]^np.int64((np.sign(np.real(z[start::Ns]))+<NUM_LIT:1>)/<NUM_LIT:2>))/float(len(z[start::Ns]))<EOL>return Pe_hat<EOL>

A simple bit error counting function.

In its present form this function counts bit errors between
hard decision BPSK bits in +/-1 form and compares them with
0/1 binary data that was transmitted. Timing between the Tx
and Rx data is the responsibility of the user. An enhanced
version of this function, which features automatic synching
will be created in the future.

Parameters
----------
z : ndarray of hard decision BPSK data prior to symbol spaced sampling
data : ndarray of reference bits in 1/0 format
start : timing reference for the received
Ns : the number of samples per symbol

Returns
-------
Pe_hat : the estimated probability of a bit error

Notes
-----
The Tx and Rx data streams are exclusive-or'd and the then the bit errors
are summed, and finally divided by the number of bits observed to form an
estimate of the bit error probability. This function needs to be
enhanced to be more useful.

Examples
--------
>>> from scipy import signal
>>> x,b, data = NRZ_bits(1000,10)
>>> # set Eb/N0 to 8 dB
>>>  y = cpx_AWGN(x,8,10)
>>> # matched filter the signal
>>> z = signal.lfilter(b,1,y)
>>> # make bit decisions at 10 and Ns multiples thereafter
>>> Pe_hat = bit_errors(z,data,10,10)


w = np.sqrt(Ns*np.var(x)*<NUM_LIT:10>**(-EsN0/<NUM_LIT>)/<NUM_LIT>)*(np.random.randn(len(x)) + <NUM_LIT>*np.random.randn(len(x)))                            <EOL>return x+w<EOL>

Apply white Gaussian noise to a digital communications signal.

This function represents a complex baseband white Gaussian noise
digital communications channel. The input signal array may be real
or complex.

Parameters
----------
x : ndarray noise free complex baseband input signal.
EsNO : set the channel Es/N0 (Eb/N0 for binary) level in dB
Ns : number of samples per symbol (bit)

Returns
-------
y : ndarray x with additive noise added.

Notes
-----
Set the channel energy per symbol-to-noise power spectral
density ratio (Es/N0) in dB.

Examples
--------
>>> x,b, data = NRZ_bits(1000,10)
>>> # set Eb/N0 = 10 dB
>>> y = cpx_AWGN(x,10,10)


Px,f = pylab.mlab.psd(x,NFFT,Fs)<EOL>return Px.flatten(), f<EOL>

A local version of NumPy's PSD function that returns the plot arrays.

A mlab.psd wrapper function that returns two ndarrays;
makes no attempt to auto plot anything.

Parameters
----------
x : ndarray input signal
NFFT : a power of two, e.g., 2**10 = 1024
Fs : the sampling rate in Hz

Returns
-------
Px : ndarray of the power spectrum estimate
f : ndarray of frequency values

Notes
-----
This function makes it easier to overlay spectrum plots because
you have better control over the axis scaling than when using psd()
in the autoscale mode.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import log10
>>> from sk_dsp_comm import sigsys as ss
>>> x,b, data = ss.NRZ_bits(10000,10)
>>> Px,f = ss.my_psd(x,2**10,10)
>>> plt.plot(f, 10*log10(Px))
>>> plt.ylabel("Power Spectral Density (dB)")
>>> plt.xlabel("Frequency (Hz)")
>>> plt.show()


m24 = interp24(m)<EOL>t192 = np.arange(len(m24))/<NUM_LIT><EOL>m_max = np.max(np.abs(m24))<EOL>x192 = (<NUM_LIT:1> + a_mod*m24/m_max)*np.cos(<NUM_LIT:2>*np.pi*fc*t192) <EOL>return x192, t192, m24<EOL>

AM transmitter for Case Study of Chapter 17.

Assume input is sampled at 8 Ksps and upsampling
by 24 is performed to arrive at fs_out = 192 Ksps.

Parameters
----------
m : ndarray of the input message signal
a_mod : AM modulation index, between 0 and 1
fc : the carrier frequency in Hz

Returns
-------
x192 : ndarray of the upsampled by 24 and modulated carrier
t192 : ndarray of the upsampled by 24 time axis
m24 : ndarray of the upsampled by 24 message signal

Notes
-----
The sampling rate of the input signal is assumed to be 8 kHz.

Examples
--------
>>> n = arange(0,1000)
>>> # 1 kHz message signal
>>> m = cos(2*pi*1000/8000.*n)
>>> x192, t192 = am_tx(m,0.8,fc=75e3)


x_edet192 = env_det(x192)<EOL>m_rx8 = deci24(x_edet192)<EOL>m_rx8 -= np.mean(m_rx8)<EOL>t8 = np.arange(len(m_rx8))/<NUM_LIT><EOL>"""<STR_LIT>"""<EOL>b192,a192 = signal.butter(<NUM_LIT:5>,<NUM_LIT:2>*<NUM_LIT>/<NUM_LIT>)<EOL>m_rx192 = signal.lfilter(b192,a192,x_edet192)<EOL>m_rx192 = signal.lfilter(b192,a192,m_rx192)<EOL>m_rx192 -= np.mean(m_rx192)<EOL>return m_rx8,t8,m_rx192,x_edet192<EOL>

AM envelope detector receiver for the Chapter 17 Case Study

The receiver bandpass filter is not included in this function.

Parameters
----------
x192 : ndarray of the AM signal at sampling rate 192 ksps

Returns
-------
m_rx8 : ndarray of the demodulated message at 8 ksps
t8 : ndarray of the time axis at 8 ksps
m_rx192 : ndarray of the demodulated output at 192 ksps
x_edet192 : ndarray of the envelope detector output at 192 ksps

Notes
-----
The bandpass filter needed at the receiver front-end can be designed
using b_bpf,a_bpf = :func:`am_rx_BPF`.

Examples
--------
>>> import numpy as np
>>> n = np.arange(0,1000)
>>> # 1 kHz message signal
>>> m = np.cos(2*np.pi*1000/8000.*n)
>>> m_rx8,t8,m_rx192,x_edet192 = am_rx(x192)


def am_rx_BPF(N_order = <NUM_LIT:7>, ripple_dB = <NUM_LIT:1>, B = <NUM_LIT>, fs = <NUM_LIT>):

b_bpf,a_bpf = signal.cheby1(N_order,ripple_dB,<NUM_LIT:2>*np.array([<NUM_LIT>-B/<NUM_LIT>,<NUM_LIT>+B/<NUM_LIT>])/fs,'<STR_LIT>')<EOL>return b_bpf,a_bpf<EOL>

Bandpass filter design for the AM receiver Case Study of Chapter 17.

Design a 7th-order Chebyshev type 1 bandpass filter to remove/reduce
adjacent channel intereference at the envelope detector input.

Parameters
----------
N_order : the filter order (default = 7)
ripple_dB : the passband ripple in dB (default = 1)
B : the RF bandwidth (default = 10e3)
fs : the sampling frequency

Returns
-------
b_bpf : ndarray of the numerator filter coefficients
a_bpf : ndarray of the denominator filter coefficients

Examples
--------
>>> from scipy import signal
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> import sk_dsp_comm.sigsys as ss
>>> # Use the default values
>>> b_bpf,a_bpf = ss.am_rx_BPF()

Pole-zero plot of the filter.

>>> ss.zplane(b_bpf,a_bpf)
>>> plt.show()

Plot of the frequency response.

>>> f = np.arange(0,192/2.,.1)
>>> w, Hbpf = signal.freqz(b_bpf,a_bpf,2*np.pi*f/192)
>>> plt.plot(f*10,20*np.log10(abs(Hbpf)))
>>> plt.axis([0,1920/2.,-80,10])
>>> plt.ylabel("Power Spectral Density (dB)")
>>> plt.xlabel("Frequency (kHz)")
>>> plt.show()


y = np.zeros(len(x))<EOL>for k,xx in enumerate(x):<EOL><INDENT>if xx >= <NUM_LIT:0>:<EOL><INDENT>y[k] = xx<EOL><DEDENT><DEDENT>return y<EOL>

Ideal envelope detector.

This function retains the positive half cycles of the input signal.

Parameters
----------
x : ndarray of the input sugnal

Returns
-------
y : ndarray of the output signal

Examples
--------
>>> n = arange(0,100)
>>> # 1 kHz message signal
>>> m = cos(2*pi*1000/8000.*n)
>>> x192, t192, m24 = am_tx(m,0.8,fc=75e3)
>>> y = env_det(x192)


<EOL>b2,a2 = signal.butter(<NUM_LIT:10>,<NUM_LIT:1>/<NUM_LIT>)<EOL>y1 = upsample(x,<NUM_LIT:2>)<EOL>y1 = signal.lfilter(b2,a2,<NUM_LIT:2>*y1)<EOL>b3,a3 = signal.butter(<NUM_LIT:10>,<NUM_LIT:1>/<NUM_LIT>)<EOL>y2 = upsample(y1,<NUM_LIT:3>)<EOL>y2 = signal.lfilter(b3,a3,<NUM_LIT:3>*y2)<EOL>b4,a4 = signal.butter(<NUM_LIT:10>,<NUM_LIT:1>/<NUM_LIT>)<EOL>y3 = upsample(y2,<NUM_LIT:4>)<EOL>y3 = signal.lfilter(b4,a4,<NUM_LIT:4>*y3)<EOL>return y3<EOL>

Interpolate by L = 24 using Butterworth filters.

The interpolation is done using three stages. Upsample by
L = 2 and lowpass filter, upsample by 3 and lowpass filter, then
upsample by L = 4 and lowpass filter. In all cases the lowpass
filter is a 10th-order Butterworth lowpass.

Parameters
----------
x : ndarray of the input signal

Returns
-------
y : ndarray of the output signal

Notes
-----
The cutoff frequency of the lowpass filters is 1/2, 1/3, and 1/4 to
track the upsampling by 2, 3, and 4 respectively.

Examples
--------
>>> y = interp24(x)


<EOL>b2,a2 = signal.butter(<NUM_LIT:10>,<NUM_LIT:1>/<NUM_LIT>)<EOL>y1 = signal.lfilter(b2,a2,x)<EOL>y1 = downsample(y1,<NUM_LIT:2>)<EOL>b3,a3 = signal.butter(<NUM_LIT:10>,<NUM_LIT:1>/<NUM_LIT>)<EOL>y2 = signal.lfilter(b3,a3,y1)<EOL>y2 = downsample(y2,<NUM_LIT:3>)<EOL>b4,a4 = signal.butter(<NUM_LIT:10>,<NUM_LIT:1>/<NUM_LIT>)<EOL>y3 = signal.lfilter(b4,a4,y2)<EOL>y3 = downsample(y3,<NUM_LIT:4>)<EOL>return y3<EOL>

Decimate by L = 24 using Butterworth filters.

The decimation is done using two three stages. Downsample sample by
L = 2 and lowpass filter, downsample by 3 and lowpass filter, then
downsample by L = 4 and lowpass filter. In all cases the lowpass
filter is a 10th-order Butterworth lowpass.

Parameters
----------
x : ndarray of the input signal

Returns
-------
y : ndarray of the output signal

Notes
-----
The cutoff frequency of the lowpass filters is 1/2, 1/3, and 1/4 to
track the upsampling by 2, 3, and 4 respectively.

Examples
--------
>>> y = deci24(x)


N_input = len(x)<EOL>y = np.hstack((x.reshape(N_input,<NUM_LIT:1>),np.zeros((N_input, int(L-<NUM_LIT:1>)))))<EOL>y = y.flatten()<EOL>return y<EOL>

Upsample by factor L

Insert L - 1 zero samples in between each input sample.

Parameters
----------
x : ndarray of input signal values
L : upsample factor

Returns
-------
y : ndarray of the output signal values

Examples
--------
>>> y = upsample(x,3)


if not isinstance(M, int):<EOL><INDENT>raise TypeError("<STR_LIT>")<EOL><DEDENT>x = x[<NUM_LIT:0>:int(np.floor(len(x)/M))*M]<EOL>x = x.reshape((int(np.floor(len(x)/M)),M))<EOL>y = x[:,p]<EOL>return y<EOL>

Downsample by factor M

Keep every Mth sample of the input. The phase of the input samples
kept can be selected.

Parameters
----------
x : ndarray of input signal values
M : downsample factor
p : phase of decimated value, 0 (default), 1, ..., M-1

Returns
-------
y : ndarray of the output signal values

Examples
--------
>>> y = downsample(x,3)
>>> y = downsample(x,3,1)


def unique_cpx_roots(rlist,tol = <NUM_LIT>):

uniq = [rlist[<NUM_LIT:0>]]<EOL>mult = [<NUM_LIT:1>]<EOL>for k in range(<NUM_LIT:1>,len(rlist)):<EOL><INDENT>N_uniq = len(uniq)<EOL>for m in range(N_uniq):<EOL><INDENT>if abs(rlist[k]-uniq[m]) <= tol:<EOL><INDENT>mult[m] += <NUM_LIT:1><EOL>uniq[m] = (uniq[m]*(mult[m]-<NUM_LIT:1>) + rlist[k])/float(mult[m])<EOL>break<EOL><DEDENT><DEDENT>uniq = np.hstack((uniq,rlist[k]))<EOL>mult = np.hstack((mult,[<NUM_LIT:1>]))<EOL><DEDENT>return np.array(uniq), np.array(mult)<EOL>

The average of the root values is used when multiplicity
is greater than one.

Mark Wickert October 2016


M = len(b) - <NUM_LIT:1><EOL>N = len(a) - <NUM_LIT:1><EOL>x_scale = <NUM_LIT>*size<EOL>y_scale = <NUM_LIT>*size   <EOL>x_off = <NUM_LIT><EOL>y_off = <NUM_LIT><EOL>if M > <NUM_LIT:0>:<EOL><INDENT>N_roots = np.roots(b)<EOL><DEDENT>if N > <NUM_LIT:0>:<EOL><INDENT>D_roots = np.roots(a)<EOL><DEDENT>if auto_scale:<EOL><INDENT>if M > <NUM_LIT:0> and N > <NUM_LIT:0>:<EOL><INDENT>size = max(np.max(np.abs(N_roots)),np.max(np.abs(D_roots)))+<NUM_LIT><EOL><DEDENT>elif M > <NUM_LIT:0>:<EOL><INDENT>size = max(np.max(np.abs(N_roots)),<NUM_LIT:1.0>)+<NUM_LIT><EOL><DEDENT>elif N > <NUM_LIT:0>:<EOL><INDENT>size = max(<NUM_LIT:1.0>,np.max(np.abs(D_roots)))+<NUM_LIT><EOL><DEDENT>else:<EOL><INDENT>size = <NUM_LIT><EOL><DEDENT><DEDENT>plt.figure(figsize=(<NUM_LIT:5>,<NUM_LIT:5>))<EOL>plt.axis('<STR_LIT>')<EOL>r = np.linspace(<NUM_LIT:0>,<NUM_LIT:2>*np.pi,<NUM_LIT:200>)<EOL>plt.plot(np.cos(r),np.sin(r),'<STR_LIT>')<EOL>plt.plot([-size,size],[<NUM_LIT:0>,<NUM_LIT:0>],'<STR_LIT>')<EOL>plt.plot([<NUM_LIT:0>,<NUM_LIT:0>],[-size,size],'<STR_LIT>')<EOL>if M > <NUM_LIT:0>:<EOL><INDENT>if detect_mult == True:<EOL><INDENT>N_uniq, N_mult = unique_cpx_roots(N_roots,tol=tol)<EOL>plt.plot(np.real(N_uniq),np.imag(N_uniq),'<STR_LIT>',mfc='<STR_LIT:None>',ms=<NUM_LIT:8>)<EOL>idx_N_mult = np.nonzero(np.ravel(N_mult><NUM_LIT:1>))[<NUM_LIT:0>]<EOL>for k in range(len(idx_N_mult)):<EOL><INDENT>x_loc = np.real(N_uniq[idx_N_mult[k]]) + x_off*x_scale<EOL>y_loc =np.imag(N_uniq[idx_N_mult[k]]) + y_off*y_scale<EOL>plt.text(x_loc,y_loc,str(N_mult[idx_N_mult[k]]),ha='<STR_LIT>',va='<STR_LIT>',fontsize=<NUM_LIT:10>)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>plt.plot(np.real(N_roots),np.imag(N_roots),'<STR_LIT>',mfc='<STR_LIT:None>',ms=<NUM_LIT:8>)                <EOL><DEDENT><DEDENT>if N > <NUM_LIT:0>:<EOL><INDENT>if detect_mult == True:<EOL><INDENT>D_uniq, D_mult=unique_cpx_roots(D_roots,tol=tol)<EOL>plt.plot(np.real(D_uniq),np.imag(D_uniq),'<STR_LIT>',ms=<NUM_LIT:8>)<EOL>idx_D_mult = np.nonzero(np.ravel(D_mult><NUM_LIT:1>))[<NUM_LIT:0>]<EOL>for k in range(len(idx_D_mult)):<EOL><INDENT>x_loc = np.real(D_uniq[idx_D_mult[k]]) + x_off*x_scale<EOL>y_loc =np.imag(D_uniq[idx_D_mult[k]]) + y_off*y_scale<EOL>plt.text(x_loc,y_loc,str(D_mult[idx_D_mult[k]]),ha='<STR_LIT>',va='<STR_LIT>',fontsize=<NUM_LIT:10>)            <EOL><DEDENT><DEDENT>else:<EOL><INDENT>plt.plot(np.real(D_roots),np.imag(D_roots),'<STR_LIT>',ms=<NUM_LIT:8>)                <EOL><DEDENT><DEDENT>if M - N < <NUM_LIT:0>:<EOL><INDENT>plt.plot(<NUM_LIT:0.0>,<NUM_LIT:0.0>,'<STR_LIT>',mfc='<STR_LIT:None>',ms=<NUM_LIT:8>)<EOL><DEDENT>elif M - N > <NUM_LIT:0>:<EOL><INDENT>plt.plot(<NUM_LIT:0.0>,<NUM_LIT:0.0>,'<STR_LIT>',ms=<NUM_LIT:8>)<EOL><DEDENT>if abs(M - N) > <NUM_LIT:1>:<EOL><INDENT>plt.text(x_off*x_scale,y_off*y_scale,str(abs(M-N)),ha='<STR_LIT>',va='<STR_LIT>',fontsize=<NUM_LIT:10>)        <EOL><DEDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL>plt.axis([-size,size,-size,size])<EOL>return M,N<EOL>

Create an z-plane pole-zero plot.

Create an z-plane pole-zero plot using the numerator
and denominator z-domain system function coefficient
ndarrays b and a respectively. Assume descending powers of z.

Parameters
----------
b : ndarray of the numerator coefficients
a : ndarray of the denominator coefficients
auto_scale : bool (default True)
size : plot radius maximum when scale = False

Returns
-------
(M,N) : tuple of zero and pole counts + plot window

Notes
-----
This function tries to identify repeated poles and zeros and will
place the multiplicity number above and to the right of the pole or zero.
The difficulty is setting the tolerance for this detection. Currently it
is set at 1e-3 via the function signal.unique_roots.

Examples
--------
>>> # Here the plot is generated using auto_scale
>>> zplane(b,a)
>>> # Here the plot is generated using manual scaling
>>> zplane(b,a,False,1.5)


y = np.zeros(len(n))<EOL>for k in range(len(n)):<EOL><INDENT>if n[k] >= <NUM_LIT:0> and n[k] < N_len-<NUM_LIT:1>:<EOL><INDENT>y[k] = n[k] + <NUM_LIT:1><EOL><DEDENT>elif n[k] >= N_len-<NUM_LIT:1> and n[k] <= <NUM_LIT:2>*N_len-<NUM_LIT:2>:<EOL><INDENT>y[k] = <NUM_LIT:2>*N_len-<NUM_LIT:1>-n[k]<EOL><DEDENT><DEDENT>return y<EOL>

The theoretical result of convolving two rectangle sequences.

The result is a triangle. The solution is
based on pure analysis. Simply coded as opposed
to efficiently coded.

Parameters
----------
n : ndarray of time axis
N_len : rectangle pulse duration

Returns
-------
y : ndarray of of output signal

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.sigsys import rect_conv
>>> n = arange(-5,20)
>>> y = rect_conv(n,6)
>>> plt.plot(n, y)
>>> plt.show()


def biquad2(w_num, r_num, w_den, r_den):

b = np.array([<NUM_LIT:1>, -<NUM_LIT:2>*r_num*np.cos(w_num), r_num**<NUM_LIT:2>])<EOL>a = np.array([<NUM_LIT:1>, -<NUM_LIT:2>*r_den*np.cos(w_den), r_den**<NUM_LIT:2>])<EOL>return b, a<EOL>

A biquadratic filter in terms of conjugate pole and zero pairs.

Parameters
----------
w_num : zero frequency (angle) in rad/sample
r_num : conjugate zeros radius
w_den : pole frequency (angle) in rad/sample
r_den : conjugate poles radius; less than 1 for stability

Returns
-------
b : ndarray of numerator coefficients
a : ndarray of denominator coefficients

Examples
--------
>>> b,a = biquad2(pi/4., 1, pi/4., 0.95)


fs, x = wavfile.read(filename)<EOL>return fs, x/<NUM_LIT><EOL>

Read a wave file.

A wrapper function for scipy.io.wavfile.read
that also includes int16 to float [-1,1] scaling.

Parameters
----------
filename : file name string

Returns
-------
fs : sampling frequency in Hz
x : ndarray of normalized to 1 signal samples

Examples
--------
>>> fs,x = from_wav('test_file.wav')


x16 = np.int16(x*<NUM_LIT>)<EOL>wavfile.write(filename, rate, x16)<EOL>

Write a wave file.

A wrapper function for scipy.io.wavfile.write
that also includes int16 scaling and conversion.
Assume input x is [-1,1] values.

Parameters
----------
filename : file name string
rate : sampling frequency in Hz

Returns
-------
Nothing : writes only the *.wav file

Examples
--------
>>> to_wav('test_file.wav', 8000, x)


def freqz_resp(b,a=[<NUM_LIT:1>],mode = '<STR_LIT>',fs=<NUM_LIT:1.0>,Npts = <NUM_LIT>,fsize=(<NUM_LIT:6>,<NUM_LIT:4>)):

f = np.arange(<NUM_LIT:0>,Npts)/(<NUM_LIT>*Npts)<EOL>w,H = signal.freqz(b,a,<NUM_LIT:2>*np.pi*f)<EOL>plt.figure(figsize=fsize)<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f*fs,<NUM_LIT:20>*np.log10(np.abs(H)))<EOL>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL><DEDENT>elif mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f*fs,np.angle(H))<EOL>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL><DEDENT>elif (mode.lower() == '<STR_LIT>') or (mode.lower() == '<STR_LIT>'):<EOL><INDENT>"""<STR_LIT>"""<EOL>theta = np.unwrap(np.angle(H))<EOL>theta2 = np.unwrap(<NUM_LIT:2>*theta)/<NUM_LIT><EOL>theta_dif = np.diff(theta2)<EOL>f_diff = np.diff(f)<EOL>Tg = -np.diff(theta2)/np.diff(w)<EOL>idx = pylab.find(<NUM_LIT:20>*np.log10(H[:-<NUM_LIT:1>]) < -<NUM_LIT>)<EOL>Tg[idx] = np.zeros(len(idx))<EOL>max_Tg = np.max(Tg)<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>max_Tg /= fs<EOL>plt.plot(f[:-<NUM_LIT:1>]*fs,Tg/fs)<EOL>plt.ylim([<NUM_LIT:0>,<NUM_LIT>*max_Tg])<EOL><DEDENT>else:<EOL><INDENT>plt.plot(f[:-<NUM_LIT:1>]*fs,Tg)<EOL>plt.ylim([<NUM_LIT:0>,<NUM_LIT>*max_Tg])<EOL><DEDENT>plt.xlabel('<STR_LIT>')<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.ylabel('<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>plt.ylabel('<STR_LIT>')<EOL><DEDENT>plt.title('<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>s1 = '<STR_LIT>'<EOL>s2 = '<STR_LIT>'<EOL>print(s1 + s2)<EOL><DEDENT>

A method for displaying digital filter frequency response magnitude,
phase, and group delay. A plot is produced using matplotlib

freq_resp(self,mode = 'dB',Npts = 1024)

A method for displaying the filter frequency response magnitude,
phase, and group delay. A plot is produced using matplotlib

freqz_resp(b,a=[1],mode = 'dB',Npts = 1024,fsize=(6,4))

    b = ndarray of numerator coefficients
    a = ndarray of denominator coefficents
 mode = display mode: 'dB' magnitude, 'phase' in radians, or
        'groupdelay_s' in samples and 'groupdelay_t' in sec,
        all versus frequency in Hz
 Npts = number of points to plot; defult is 1024
fsize = figure size; defult is (6,4) inches

Mark Wickert, January 2015


def __init__(self,M_change = <NUM_LIT:12>,fcutoff=<NUM_LIT>,N_filt_order=<NUM_LIT:8>,ftype='<STR_LIT>'):

self.M = M_change <EOL>self.fc = fcutoff*<NUM_LIT> <EOL>self.N_forder = N_filt_order<EOL>if ftype.lower() == '<STR_LIT>':<EOL><INDENT>self.b, self.a = signal.butter(self.N_forder,<NUM_LIT:2>/self.M*self.fc)<EOL><DEDENT>elif ftype.lower() == '<STR_LIT>':<EOL><INDENT>self.b, self.a = signal.cheby1(self.N_forder,<NUM_LIT>,<NUM_LIT:2>/self.M*self.fc)<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>


y = self.M*ssd.upsample(x,self.M)<EOL>y = signal.lfilter(self.b,self.a,y)<EOL>return y<EOL>


y = signal.lfilter(self.b,self.a,x)<EOL>y = ssd.downsample(y,self.M)<EOL>return y<EOL>


self.N_forder = len(b)<EOL>self.b = b<EOL>print('<STR_LIT>' % self.N_forder)<EOL>


y = signal.lfilter(self.b,[<NUM_LIT:1>],x)<EOL>return y<EOL>


y = L_change*ssd.upsample(x,L_change)<EOL>y = signal.lfilter(self.b,[<NUM_LIT:1>],y)<EOL>return y<EOL>


y = signal.lfilter(self.b,[<NUM_LIT:1>],x)<EOL>y = ssd.downsample(y,M_change)<EOL>return y<EOL>


ssd.zplane(self.b,[<NUM_LIT:1>],auto_scale,size,tol)<EOL>

Plot the poles and zeros of the FIR filter in the z-plane


self.N_forder = np.sum(np.sign(np.abs(sos[:,<NUM_LIT:2>])))+ np.sum(np.sign(np.abs(sos[:,<NUM_LIT:1>])))<EOL>self.sos = sos<EOL>print('<STR_LIT>' % self.N_forder)<EOL>


y = signal.sosfilt(self.sos,x)<EOL>return y<EOL>

Filter the signal using second-order sections


y = L_change*ssd.upsample(x,L_change)<EOL>y = signal.sosfilt(self.sos,y)<EOL>return y<EOL>


y = signal.sosfilt(self.sos,x)<EOL>y = ssd.downsample(y,M_change)<EOL>return y<EOL>


def freq_resp(self, mode= '<STR_LIT>', fs = <NUM_LIT>, ylim = [-<NUM_LIT:100>,<NUM_LIT:2>]):

iir_d.freqz_resp_cas_list([self.sos],mode,fs=fs)<EOL>pylab.grid()<EOL>pylab.ylim(ylim)<EOL>


iir_d.sos_zplane(self.sos,auto_scale,size,tol)<EOL>


<EOL>v3 = signal.lfilter([<NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>/<NUM_LIT>, <NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>/<NUM_LIT>],[<NUM_LIT:1>],x)<EOL>v2 = signal.lfilter([<NUM_LIT:0>, <NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>, <NUM_LIT:1>/<NUM_LIT>],[<NUM_LIT:1>],x)<EOL>v1 = signal.lfilter([-<NUM_LIT:1>/<NUM_LIT>, <NUM_LIT:1>, -<NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>/<NUM_LIT>],[<NUM_LIT:1>],x)<EOL>v0 = signal.lfilter([<NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>],[<NUM_LIT:1>],x)<EOL>Ts_old = <NUM_LIT:1>/float(fs_old)<EOL>Ts_new = <NUM_LIT:1>/float(fs_new)<EOL>T_end = Ts_old*(len(x)-<NUM_LIT:3>)<EOL>t_new = np.arange(<NUM_LIT:0>,T_end+Ts_old,Ts_new)<EOL>if x.dtype == np.dtype('<STR_LIT>') or x.dtype == np.dtype('<STR_LIT>'):<EOL><INDENT>y = np.zeros(len(t_new)) + <NUM_LIT>*np.zeros(len(t_new))<EOL><DEDENT>else:<EOL><INDENT>y = np.zeros(len(t_new))<EOL><DEDENT>for n in range(len(t_new)):<EOL><INDENT>n_old = int(np.floor(n*Ts_new/Ts_old))<EOL>mu = (n*Ts_new - n_old*Ts_old)/Ts_old<EOL>y[n] = ((v3[n_old+<NUM_LIT:1>]*mu + v2[n_old+<NUM_LIT:1>])*mu<EOL>+ v1[n_old+<NUM_LIT:1>])*mu + v0[n_old+<NUM_LIT:1>]<EOL><DEDENT>return y<EOL>

Parameters
----------
x : Input list representing a signal vector needing resampling.
fs_old : Starting/old sampling frequency.
fs_new : New sampling frequency.

Returns
-------
y : List representing the signal vector resampled at the new frequency.

Notes
-----

A cubic interpolator using a Farrow structure is used resample the
input data at a new sampling rate that may be an irrational multiple of
the input sampling rate.

Time alignment can be found for a integer value M, found with the following:

.. math:: f_{s,out} = f_{s,in} (M - 1) / M

The filter coefficients used here and a more comprehensive listing can be
found in H. Meyr, M. Moeneclaey, & S. Fechtel, "Digital Communication
Receivers," Wiley, 1998, Chapter 9, pp. 521-523.

Another good paper on variable interpolators is: L. Erup, F. Gardner, &
R. Harris, "Interpolation in Digital Modems--Part II: Implementation
and Performance," IEEE Comm. Trans., June 1993, pp. 998-1008.

A founding paper on the subject of interpolators is: C. W. Farrow, "A
Continuously variable Digital Delay Element," Proceedings of the IEEE
Intern. Symp. on Circuits Syst., pp. 2641-2645, June 1988.

Mark Wickert April 2003, recoded to Python November 2013

Examples
--------

The following example uses a QPSK signal with rc pulse shaping, and time alignment at M = 15.

>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm import digitalcom as dc
>>> Ns = 8
>>> Rs = 1.
>>> fsin = Ns*Rs
>>> Tsin = 1 / fsin
>>> N = 200
>>> ts = 1
>>> x, b, data = dc.MPSK_bb(N+12, Ns, 4, 'rc')
>>> x = x[12*Ns:]
>>> xxI = x.real
>>> M = 15
>>> fsout = fsin * (M-1) / M
>>> Tsout = 1. / fsout
>>> xI = dc.farrow_resample(xxI, fsin, fsin)
>>> tx = arange(0, len(xI)) / fsin
>>> yI = dc.farrow_resample(xxI, fsin, fsout)
>>> ty = arange(0, len(yI)) / fsout
>>> plt.plot(tx - Tsin, xI)
>>> plt.plot(tx[ts::Ns] - Tsin, xI[ts::Ns], 'r.')
>>> plt.plot(ty[ts::Ns] - Tsout, yI[ts::Ns], 'g.')
>>> plt.title(r'Impact of Asynchronous Sampling')
>>> plt.ylabel(r'Real Signal Amplitude')
>>> plt.xlabel(r'Symbol Rate Normalized Time')
>>> plt.xlim([0, 20])
>>> plt.grid()
>>> plt.show()


plt.figure(figsize=(<NUM_LIT:6>,<NUM_LIT:4>))<EOL>idx = np.arange(<NUM_LIT:0>,L+<NUM_LIT:1>)<EOL>plt.plot(idx,x[S:S+L+<NUM_LIT:1>],'<STR_LIT:b>')<EOL>k_max = int((len(x) - S)/L)-<NUM_LIT:1><EOL>for k in range(<NUM_LIT:1>,k_max):<EOL><INDENT>plt.plot(idx,x[S+k*L:S+L+<NUM_LIT:1>+k*L],'<STR_LIT:b>')<EOL><DEDENT>plt.grid()<EOL>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL>return <NUM_LIT:0><EOL>

Eye pattern plot of a baseband digital communications waveform.

The signal must be real, but can be multivalued in terms of the underlying
modulation scheme. Used for BPSK eye plots in the Case Study article.

Parameters
----------
x : ndarray of the real input data vector/array
L : display length in samples (usually two symbols)
S : start index

Returns
-------
None : A plot window opens containing the eye plot

Notes
-----
Increase S to eliminate filter transients.

Examples
--------
1000 bits at 10 samples per bit with 'rc' shaping.

>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm import digitalcom as dc
>>> x,b, data = dc.NRZ_bits(1000,10,'rc')
>>> dc.eye_plot(x,20,60)
>>> plt.show()


xI = np.real(x[start::Ns])<EOL>xQ = np.imag(x[start::Ns])<EOL>return xI, xQ<EOL>

Sample a baseband digital communications waveform at the symbol spacing.

Parameters
----------
x : ndarray of the input digital comm signal
Ns : number of samples per symbol (bit)
start : the array index to start the sampling

Returns
-------
xI : ndarray of the real part of x following sampling
xQ : ndarray of the imaginary part of x following sampling

Notes
-----
Normally the signal is complex, so the scatter plot contains
clusters at point  in the complex plane. For a binary signal
such as BPSK, the point centers are nominally +/-1 on the real
axis. Start is used to eliminate transients from the FIR
pulse shaping filters from appearing in the scatter plot.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm import digitalcom as dc
>>> x,b, data = dc.NRZ_bits(1000,10,'rc')

Add some noise so points are now scattered about +/-1.

>>> y = dc.cpx_AWGN(x,20,10)
>>> yI,yQ = dc.scatter(y,10,60)
>>> plt.plot(yI,yQ,'.')
>>> plt.grid()
>>> plt.xlabel('In-Phase')
>>> plt.ylabel('Quadrature')
>>> plt.axis('equal')
>>> plt.show()


plt.figure(figsize=fig_size)<EOL>N = len(x)<EOL>Mx = int(np.ceil(N/float(Nx)))<EOL>x_max = np.max(np.abs(x))<EOL>for kk in range(Mx):<EOL><INDENT>plt.plot(np.array([<NUM_LIT:0>,Nx]),-kk*Nx*np.array([<NUM_LIT:1>,<NUM_LIT:1>]),'<STR_LIT>')<EOL>plt.plot(x[kk*Nx:(kk+<NUM_LIT:1>)*Nx]/x_max*<NUM_LIT>*Nx-kk*Nx,'<STR_LIT:b>')<EOL><DEDENT>plt.axis([<NUM_LIT:0>,Nx,-Nx*(Mx-<NUM_LIT:0.5>),Nx*<NUM_LIT:0.5>])<EOL>plt.yticks(np.arange(<NUM_LIT:0>,-Nx*Mx,-Nx),np.arange(<NUM_LIT:0>,Nx*Mx,Nx))<EOL>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>return <NUM_LIT:0><EOL>

Plots the contents of real ndarray x as a vertical stacking of
strips, each of length Nx. The default figure size is (6,4) inches.
The yaxis tick labels are the starting index of each strip. The red
dashed lines correspond to zero amplitude in each strip.

strips(x,Nx,my_figsize=(6,4))

Mark Wickert April 2014


def bit_errors(tx_data,rx_data,Ncorr = <NUM_LIT>,Ntransient = <NUM_LIT:0>):

<EOL>tx_data = <NUM_LIT:2>*tx_data[Ntransient:]-<NUM_LIT:1><EOL>rx_data = <NUM_LIT:2>*rx_data[Ntransient:]-<NUM_LIT:1><EOL>R0 = np.fft.ifft(np.fft.fft(rx_data,Ncorr)*<EOL>np.conj(np.fft.fft(tx_data,Ncorr)))<EOL>R1 = np.fft.ifft(np.fft.fft(-<NUM_LIT:1>*rx_data,Ncorr)*<EOL>np.conj(np.fft.fft(tx_data,Ncorr)))<EOL>R0 = np.fft.fftshift(R0)<EOL>R1 = np.fft.fftshift(R1)<EOL>R0max = np.max(R0.real)<EOL>R1max = np.max(R1.real)<EOL>R = np.array([R0max,R1max])<EOL>Rmax = np.max(R)<EOL>kphase_max = np.where(R == Rmax)[<NUM_LIT:0>]<EOL>kmax = kphase_max[<NUM_LIT:0>]<EOL>if kmax == <NUM_LIT:0>:<EOL><INDENT>lagmax = np.where(R0.real == Rmax)[<NUM_LIT:0>] - Ncorr/<NUM_LIT:2><EOL><DEDENT>elif kmax == <NUM_LIT:1>:<EOL><INDENT>lagmax = np.where(R1.real == Rmax)[<NUM_LIT:0>] - Ncorr/<NUM_LIT:2><EOL><DEDENT>taumax = lagmax[<NUM_LIT:0>]<EOL>print('<STR_LIT>' % (kmax, taumax))<EOL>if taumax < <NUM_LIT:0>:<EOL><INDENT>tx_data = tx_data[int(-taumax):]<EOL>tx_data = tx_data[:min(len(tx_data),len(rx_data))]<EOL>rx_data = (-<NUM_LIT:1>)**kmax*rx_data[:len(tx_data)]<EOL><DEDENT>else:<EOL><INDENT>rx_data = (-<NUM_LIT:1>)**kmax * rx_data[int(taumax):]<EOL>rx_data = rx_data[:min(len(tx_data),len(rx_data))]<EOL>tx_data = tx_data[:len(rx_data)]<EOL><DEDENT>Bit_count = len(tx_data)<EOL>tx_I = np.int16((tx_data.real + <NUM_LIT:1>)/<NUM_LIT:2>)<EOL>rx_I = np.int16((rx_data.real + <NUM_LIT:1>)/<NUM_LIT:2>)<EOL>Bit_errors = tx_I ^ rx_I<EOL>return Bit_count,np.sum(Bit_errors)<EOL>

Count bit errors between a transmitted and received BPSK signal.
Time delay between streams is detected as well as ambiquity resolution
due to carrier phase lock offsets of :math:`k*\\pi`, k=0,1.
The ndarray tx_data is Tx 0/1 bits as real numbers I.
The ndarray rx_data is Rx 0/1 bits as real numbers I.
Note: Ncorr needs to be even


<EOL>if pulse.lower() == '<STR_LIT:src>':<EOL><INDENT>b = sqrt_rc_imp(Ns,alpha,<NUM_LIT:6>)<EOL><DEDENT>elif pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = rc_imp(Ns,alpha,<NUM_LIT:6>)    <EOL><DEDENT>elif pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = np.ones(int(Ns)) <EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>if mod_type.lower() == '<STR_LIT>':<EOL><INDENT>M = <NUM_LIT:2> <EOL><DEDENT>elif mod_type.lower() == '<STR_LIT>':<EOL><INDENT>M = <NUM_LIT:4><EOL><DEDENT>elif mod_type.lower() == '<STR_LIT>':<EOL><INDENT>M = <NUM_LIT:8><EOL><DEDENT>elif mod_type.lower() == '<STR_LIT>':<EOL><INDENT>M = <NUM_LIT:16><EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>xI = np.random.randint(<NUM_LIT:0>,M,N_symb)<EOL>xI = <NUM_LIT:2>*xI - (M-<NUM_LIT:1>)<EOL>xQ = np.random.randint(<NUM_LIT:0>,M,N_symb)<EOL>xQ = <NUM_LIT:2>*xQ - (M-<NUM_LIT:1>)<EOL>symbI = np.hstack((xI.reshape(N_symb,<NUM_LIT:1>),np.zeros((N_symb,int(Ns)-<NUM_LIT:1>))))<EOL>symbI = symbI.flatten()<EOL>symbQ = np.hstack((xQ.reshape(N_symb,<NUM_LIT:1>),np.zeros((N_symb,int(Ns)-<NUM_LIT:1>))))<EOL>symbQ = symbQ.flatten()<EOL>symb = symbI + <NUM_LIT>*symbQ<EOL>if M > <NUM_LIT:2>:<EOL><INDENT>symb /= (M-<NUM_LIT:1>)<EOL><DEDENT>x = signal.lfilter(b,<NUM_LIT:1>,symb)<EOL>x = x.flatten() <EOL>b = b/sum(b)<EOL>return x, b, xI+<NUM_LIT>*xQ<EOL>

QAM_BB_TX: A complex baseband transmitter
x,b,tx_data = QAM_bb(K,Ns,M)

//////////// Inputs //////////////////////////////////////////////////
  N_symb = the number of symbols to process
      Ns = number of samples per symbol
mod_type = modulation type: qpsk, 16qam, 64qam, or 256qam
   alpha = squareroot raised codine pulse shape bandwidth factor.
           For DOCSIS alpha = 0.12 to 0.18. In general alpha can
           range over 0 < alpha < 1.
     SRC = pulse shape: 0-> rect, 1-> SRC
//////////// Outputs /////////////////////////////////////////////////
       x = complex baseband digital modulation
       b = transmitter shaping filter, rectangle or SRC
 tx_data = xI+1j*xQ = inphase symbol sequence +
           1j*quadrature symbol sequence

Mark Wickert November 2014


def QAM_SEP(tx_data,rx_data,mod_type,Ncorr = <NUM_LIT>,Ntransient = <NUM_LIT:0>,SEP_disp=True):

<EOL>tx_data = tx_data[Ntransient:]<EOL>rx_data = rx_data[Ntransient:]<EOL>Nmin = min([len(tx_data),len(rx_data)])<EOL>tx_data = tx_data[:Nmin]<EOL>rx_data = rx_data[:Nmin]<EOL>if mod_type.lower() == '<STR_LIT>':<EOL><INDENT>M = <NUM_LIT:2> <EOL><DEDENT>elif mod_type.lower() == '<STR_LIT>':<EOL><INDENT>M = <NUM_LIT:4><EOL><DEDENT>elif mod_type.lower() == '<STR_LIT>':<EOL><INDENT>M = <NUM_LIT:8><EOL><DEDENT>elif mod_type.lower() == '<STR_LIT>':<EOL><INDENT>M = <NUM_LIT:16><EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>rx_data = np.rint((M-<NUM_LIT:1>)*(rx_data + (<NUM_LIT:1>+<NUM_LIT>))/<NUM_LIT>)<EOL>s1r = np.nonzero(np.ravel(rx_data.real > M - <NUM_LIT:1>))[<NUM_LIT:0>]<EOL>s2r = np.nonzero(np.ravel(rx_data.real < <NUM_LIT:0>))[<NUM_LIT:0>]<EOL>rx_data.real[s1r] = (M - <NUM_LIT:1>)*np.ones(len(s1r))<EOL>rx_data.real[s2r] = np.zeros(len(s2r))<EOL>s1i = np.nonzero(np.ravel(rx_data.imag > M - <NUM_LIT:1>))[<NUM_LIT:0>]<EOL>s2i = np.nonzero(np.ravel(rx_data.imag < <NUM_LIT:0>))[<NUM_LIT:0>]<EOL>rx_data.imag[s1i] = (M - <NUM_LIT:1>)*np.ones(len(s1i))<EOL>rx_data.imag[s2i] = np.zeros(len(s2i))<EOL>rx_data = <NUM_LIT:2>*rx_data - (M - <NUM_LIT:1>)*(<NUM_LIT:1> + <NUM_LIT>)<EOL>R0,lags = xcorr(rx_data,tx_data,Ncorr)<EOL>R1,lags = xcorr(rx_data*(<NUM_LIT>)**<NUM_LIT:1>,tx_data,Ncorr) <EOL>R2,lags = xcorr(rx_data*(<NUM_LIT>)**<NUM_LIT:2>,tx_data,Ncorr) <EOL>R3,lags = xcorr(rx_data*(<NUM_LIT>)**<NUM_LIT:3>,tx_data,Ncorr) <EOL>R0max = np.max(R0.real)<EOL>R1max = np.max(R1.real)<EOL>R2max = np.max(R2.real)<EOL>R3max = np.max(R3.real)<EOL>R = np.array([R0max,R1max,R2max,R3max])<EOL>Rmax = np.max(R)<EOL>kphase_max = np.where(R == Rmax)[<NUM_LIT:0>]<EOL>kmax = kphase_max[<NUM_LIT:0>]<EOL>if kmax == <NUM_LIT:0>:<EOL><INDENT>lagmax = lags[np.where(R0.real == Rmax)[<NUM_LIT:0>]]<EOL><DEDENT>elif kmax == <NUM_LIT:1>:<EOL><INDENT>lagmax = lags[np.where(R1.real == Rmax)[<NUM_LIT:0>]]<EOL><DEDENT>elif kmax == <NUM_LIT:2>:<EOL><INDENT>lagmax = lags[np.where(R2.real == Rmax)[<NUM_LIT:0>]]<EOL><DEDENT>elif kmax == <NUM_LIT:3>:<EOL><INDENT>lagmax = lags[np.where(R3.real == Rmax)[<NUM_LIT:0>]]<EOL><DEDENT>taumax = lagmax[<NUM_LIT:0>]<EOL>if SEP_disp:<EOL><INDENT>print('<STR_LIT>' % (kmax, taumax))<EOL><DEDENT>if taumax < <NUM_LIT:0>:<EOL><INDENT>tx_data = tx_data[-taumax:]<EOL>tx_data = tx_data[:min(len(tx_data),len(rx_data))]<EOL>rx_data = (<NUM_LIT>)**kmax*rx_data[:len(tx_data)]<EOL><DEDENT>else:<EOL><INDENT>rx_data = (<NUM_LIT>)**kmax*rx_data[taumax:]<EOL>rx_data = rx_data[:min(len(tx_data),len(rx_data))]<EOL>tx_data = tx_data[:len(rx_data)]<EOL><DEDENT>errors = np.int16(abs(rx_data-tx_data))<EOL>idx = np.nonzero(np.ravel(errors != <NUM_LIT:0>))[<NUM_LIT:0>]<EOL>if SEP_disp:<EOL><INDENT>print('<STR_LIT>'% (len(errors), len(idx), len(idx)/float(len(errors))))<EOL><DEDENT>return  len(errors), len(idx), len(idx)/float(len(errors))<EOL>

Nsymb, Nerr, SEP_hat =
QAM_symb_errors(tx_data,rx_data,mod_type,Ncorr = 1024,Ntransient = 0)

Count symbol errors between a transmitted and received QAM signal.
The received symbols are assumed to be soft values on a unit square.
Time delay between streams is detected.
The ndarray tx_data is Tx complex symbols.
The ndarray rx_data is Rx complex symbols.
Note: Ncorr needs to be even


def GMSK_bb(N_bits, Ns, MSK = <NUM_LIT:0>,BT = <NUM_LIT>):

x, b, data = NRZ_bits(N_bits,Ns)<EOL>M = <NUM_LIT:4><EOL>n = np.arange(-M*Ns,M*Ns+<NUM_LIT:1>)<EOL>p = np.exp(-<NUM_LIT:2>*np.pi**<NUM_LIT:2>*BT**<NUM_LIT:2>/np.log(<NUM_LIT:2>)*(n/float(Ns))**<NUM_LIT:2>);<EOL>p = p/np.sum(p);<EOL>if MSK != <NUM_LIT:0>:<EOL><INDENT>x = signal.lfilter(p,<NUM_LIT:1>,x)<EOL><DEDENT>y = np.exp(<NUM_LIT>*np.pi/<NUM_LIT:2>*np.cumsum(x)/Ns)<EOL>return y, data<EOL>

MSK/GMSK Complex Baseband Modulation
x,data = gmsk(N_bits, Ns, BT = 0.35, MSK = 0)

Parameters
----------
N_bits : number of symbols processed
Ns : the number of samples per bit
MSK : 0 for no shaping which is standard MSK, MSK <> 0 --> GMSK is generated.
BT : premodulation Bb*T product which sets the bandwidth of the Gaussian lowpass filter

Mark Wickert Python version November 2014


def MPSK_bb(N_symb,Ns,M,pulse='<STR_LIT>',alpha = <NUM_LIT>,MM=<NUM_LIT:6>):

data = np.random.randint(<NUM_LIT:0>,M,N_symb) <EOL>xs = np.exp(<NUM_LIT>*<NUM_LIT:2>*np.pi/M*data)<EOL>x = np.hstack((xs.reshape(N_symb,<NUM_LIT:1>),np.zeros((N_symb,int(Ns)-<NUM_LIT:1>))))<EOL>x =x.flatten()<EOL>if pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = np.ones(int(Ns))<EOL><DEDENT>elif pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = rc_imp(Ns,alpha,MM)<EOL><DEDENT>elif pulse.lower() == '<STR_LIT:src>':<EOL><INDENT>b = sqrt_rc_imp(Ns,alpha,MM)<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>x = signal.lfilter(b,<NUM_LIT:1>,x)<EOL>if M == <NUM_LIT:4>:<EOL><INDENT>x = x*np.exp(<NUM_LIT>*np.pi/<NUM_LIT:4>); <EOL><DEDENT>return x,b/float(Ns),data<EOL>

Generate a complex baseband MPSK signal with pulse shaping.

Parameters
----------
N_symb : number of MPSK symbols to produce
Ns : the number of samples per bit,
M : MPSK modulation order, e.g., 4, 8, 16, ...
pulse_type : 'rect' , 'rc', 'src' (default 'rect')
alpha : excess bandwidth factor(default 0.25)
MM : single sided pulse duration (default = 6)

Returns
-------
x : ndarray of the MPSK signal values
b : ndarray of the pulse shape
data : ndarray of the underlying data bits

Notes
-----
Pulse shapes include 'rect' (rectangular), 'rc' (raised cosine),
'src' (root raised cosine). The actual pulse length is 2*M+1 samples.
This function is used by BPSK_tx in the Case Study article.

Examples
--------
>>> from sk_dsp_comm import digitalcom as dc
>>> import scipy.signal as signal
>>> import matplotlib.pyplot as plt
>>> x,b,data = dc.MPSK_bb(500,10,8,'src',0.35)
>>> # Matched filter received signal x
>>> y = signal.lfilter(b,1,x)
>>> plt.plot(y.real[12*10:],y.imag[12*10:])
>>> plt.xlabel('In-Phase')
>>> plt.ylabel('Quadrature')
>>> plt.axis('equal')
>>> # Sample once per symbol
>>> plt.plot(y.real[12*10::10],y.imag[12*10::10],'r.')
>>> plt.show()


Ns = int(np.round(fs/Rs))<EOL>print('<STR_LIT>', Ns)<EOL>print('<STR_LIT>', fs/float(Ns))<EOL>print('<STR_LIT>', EsN0, '<STR_LIT>')<EOL>print('<STR_LIT>', phase, '<STR_LIT>')<EOL>print('<STR_LIT>', pulse)<EOL>x, b, data = QPSK_bb(N_symb,Ns,lfsr_len,pulse)<EOL>x = cpx_AWGN(x,EsN0,Ns)<EOL>n = np.arange(len(x))<EOL>xc = x*np.exp(<NUM_LIT>*<NUM_LIT:2>*np.pi*fc/float(fs)*n) * np.exp(<NUM_LIT>*phase)<EOL>return xc, b, data<EOL>


def QPSK_BEP(tx_data,rx_data,Ncorr = <NUM_LIT>,Ntransient = <NUM_LIT:0>):

<EOL>tx_data = tx_data[Ntransient:]<EOL>rx_data = rx_data[Ntransient:]<EOL>R0 = np.fft.ifft(np.fft.fft(rx_data,Ncorr)*<EOL>np.conj(np.fft.fft(tx_data,Ncorr)))<EOL>R1 = np.fft.ifft(np.fft.fft(<NUM_LIT>*rx_data,Ncorr)*<EOL>np.conj(np.fft.fft(tx_data,Ncorr)))<EOL>R2 = np.fft.ifft(np.fft.fft(-<NUM_LIT:1>*rx_data,Ncorr)*<EOL>np.conj(np.fft.fft(tx_data,Ncorr)))<EOL>R3 = np.fft.ifft(np.fft.fft(-<NUM_LIT>*rx_data,Ncorr)*<EOL>np.conj(np.fft.fft(tx_data,Ncorr)))<EOL>R0 = np.fft.fftshift(R0)<EOL>R1 = np.fft.fftshift(R1)<EOL>R2 = np.fft.fftshift(R2)<EOL>R3 = np.fft.fftshift(R3)<EOL>R0max = np.max(R0.real)<EOL>R1max = np.max(R1.real)<EOL>R2max = np.max(R2.real)<EOL>R3max = np.max(R3.real)<EOL>R = np.array([R0max,R1max,R2max,R3max])<EOL>Rmax = np.max(R)<EOL>kphase_max = np.where(R == Rmax)[<NUM_LIT:0>]<EOL>kmax = kphase_max[<NUM_LIT:0>]<EOL>if kmax == <NUM_LIT:0>:<EOL><INDENT>lagmax = np.where(R0.real == Rmax)[<NUM_LIT:0>] - Ncorr/<NUM_LIT:2><EOL><DEDENT>elif kmax == <NUM_LIT:1>:<EOL><INDENT>lagmax = np.where(R1.real == Rmax)[<NUM_LIT:0>] - Ncorr/<NUM_LIT:2><EOL><DEDENT>elif kmax == <NUM_LIT:2>: <EOL><INDENT>lagmax = np.where(R2.real == Rmax)[<NUM_LIT:0>] - Ncorr/<NUM_LIT:2><EOL><DEDENT>elif kmax == <NUM_LIT:3>:<EOL><INDENT>lagmax = np.where(R3.real == Rmax)[<NUM_LIT:0>] - Ncorr/<NUM_LIT:2><EOL><DEDENT>taumax = lagmax[<NUM_LIT:0>]<EOL>print('<STR_LIT>' % (kmax, taumax))<EOL>if taumax < <NUM_LIT:0>:<EOL><INDENT>tx_data = tx_data[-taumax:]<EOL>tx_data = tx_data[:min(len(tx_data),len(rx_data))]<EOL>rx_data = <NUM_LIT>**kmax*rx_data[:len(tx_data)]<EOL><DEDENT>else:<EOL><INDENT>rx_data = <NUM_LIT>**kmax*rx_data[taumax:]<EOL>rx_data = rx_data[:min(len(tx_data),len(rx_data))]<EOL>tx_data = tx_data[:len(rx_data)]<EOL><DEDENT>S_count = len(tx_data)<EOL>tx_I = np.int16((tx_data.real + <NUM_LIT:1>)/<NUM_LIT:2>)<EOL>tx_Q = np.int16((tx_data.imag + <NUM_LIT:1>)/<NUM_LIT:2>)<EOL>rx_I = np.int16((rx_data.real + <NUM_LIT:1>)/<NUM_LIT:2>)<EOL>rx_Q = np.int16((rx_data.imag + <NUM_LIT:1>)/<NUM_LIT:2>)<EOL>I_errors = tx_I ^ rx_I<EOL>Q_errors = tx_Q ^ rx_Q<EOL>S_errors = I_errors | Q_errors<EOL>return S_count,np.sum(I_errors),np.sum(Q_errors),np.sum(S_errors)<EOL>

Count bit errors between a transmitted and received QPSK signal.
Time delay between streams is detected as well as ambiquity resolution
due to carrier phase lock offsets of :math:`k*\\frac{\\pi}{4}`, k=0,1,2,3.
The ndarray sdata is Tx +/-1 symbols as complex numbers I + j*Q.
The ndarray data is Rx +/-1 symbols as complex numbers I + j*Q.
Note: Ncorr needs to be even


def BPSK_BEP(tx_data,rx_data,Ncorr = <NUM_LIT>,Ntransient = <NUM_LIT:0>):

<EOL>tx_data = tx_data[Ntransient:]<EOL>rx_data = rx_data[Ntransient:]<EOL>R0 = np.fft.ifft(np.fft.fft(rx_data,Ncorr)*<EOL>np.conj(np.fft.fft(tx_data,Ncorr)))<EOL>R1 = np.fft.ifft(np.fft.fft(-<NUM_LIT:1>*rx_data,Ncorr)*<EOL>np.conj(np.fft.fft(tx_data,Ncorr)))<EOL>R0 = np.fft.fftshift(R0)<EOL>R1 = np.fft.fftshift(R1)<EOL>R0max = np.max(R0.real)<EOL>R1max = np.max(R1.real)<EOL>R = np.array([R0max,R1max])<EOL>Rmax = np.max(R)<EOL>kphase_max = np.where(R == Rmax)[<NUM_LIT:0>]<EOL>kmax = kphase_max[<NUM_LIT:0>]<EOL>if kmax == <NUM_LIT:0>:<EOL><INDENT>lagmax = np.where(R0.real == Rmax)[<NUM_LIT:0>] - Ncorr/<NUM_LIT:2><EOL><DEDENT>elif kmax == <NUM_LIT:1>:<EOL><INDENT>lagmax = np.where(R1.real == Rmax)[<NUM_LIT:0>] - Ncorr/<NUM_LIT:2><EOL><DEDENT>taumax = int(lagmax[<NUM_LIT:0>])<EOL>print('<STR_LIT>' % (kmax, taumax))<EOL>if taumax < <NUM_LIT:0>:<EOL><INDENT>tx_data = tx_data[-taumax:]<EOL>tx_data = tx_data[:min(len(tx_data),len(rx_data))]<EOL>rx_data = (-<NUM_LIT:1>)**kmax*rx_data[:len(tx_data)]<EOL><DEDENT>else:<EOL><INDENT>rx_data = (-<NUM_LIT:1>)**kmax*rx_data[taumax:]<EOL>rx_data = rx_data[:min(len(tx_data),len(rx_data))]<EOL>tx_data = tx_data[:len(rx_data)]<EOL><DEDENT>S_count = len(tx_data)<EOL>tx_I = np.int16((tx_data.real + <NUM_LIT:1>)/<NUM_LIT:2>)<EOL>rx_I = np.int16((rx_data.real + <NUM_LIT:1>)/<NUM_LIT:2>)<EOL>I_errors = tx_I ^ rx_I<EOL>S_errors = I_errors<EOL>return S_count,np.sum(S_errors)<EOL>

Count bit errors between a transmitted and received BPSK signal.
Time delay between streams is detected as well as ambiquity resolution
due to carrier phase lock offsets of :math:`k*\\pi`, k=0,1.
The ndarray tx_data is Tx +/-1 symbols as real numbers I.
The ndarray rx_data is Rx +/-1 symbols as real numbers I.
Note: Ncorr needs to be even


x0,b,data0 = NRZ_bits(N_bits,Ns,pulse,alpha,M)<EOL>x1p,b,data1p = NRZ_bits(N_bits,Ns,pulse,alpha,M)<EOL>x1m,b,data1m = NRZ_bits(N_bits,Ns,pulse,alpha,M)<EOL>n = np.arange(len(x0))<EOL>x1p = x1p*np.exp(<NUM_LIT>*<NUM_LIT:2>*np.pi*ach_fc/float(Ns)*n)<EOL>x1m = x1m*np.exp(-<NUM_LIT>*<NUM_LIT:2>*np.pi*ach_fc/float(Ns)*n)<EOL>ach_lvl = <NUM_LIT:10>**(ach_lvl_dB/<NUM_LIT>)<EOL>return x0 + ach_lvl*(x1p + x1m), b, data0<EOL>

Generates biphase shift keyed (BPSK) transmitter with adjacent channel interference.

Generates three BPSK signals with rectangular or square root raised cosine (SRC)
pulse shaping of duration N_bits and Ns samples per bit. The desired signal is
centered on f = 0, which the adjacent channel signals to the left and right
are also generated at dB level relative to the desired signal. Used in the
digital communications Case Study supplement.

Parameters
----------
N_bits : the number of bits to simulate
Ns : the number of samples per bit
ach_fc : the frequency offset of the adjacent channel signals (default 2.0)
ach_lvl_dB : the level of the adjacent channel signals in dB (default -100)
pulse : the pulse shape 'rect' or 'src'
alpha : square root raised cosine pulse shape factor (default = 0.25)
M : square root raised cosine pulse truncation factor (default = 6)

Returns
-------
x : ndarray of the composite signal x0 + ach_lvl*(x1p + x1m)
b : the transmit pulse shape
data0 : the data bits used to form the desired signal; used for error checking

Notes
-----

Examples
--------
>>> x,b,data0 = BPSK_tx(1000,10,'src')


<EOL>n = np.arange(-M*Ns,M*Ns+<NUM_LIT:1>)<EOL>b = np.zeros(len(n))<EOL>a = alpha<EOL>Ns *= <NUM_LIT:1.0><EOL>for i in range(len(n)):<EOL><INDENT>if (<NUM_LIT:1> - <NUM_LIT:4>*(a*n[i]/Ns)**<NUM_LIT:2>) == <NUM_LIT:0>:<EOL><INDENT>b[i] = np.pi/<NUM_LIT:4>*np.sinc(<NUM_LIT:1>/(<NUM_LIT>*a))<EOL><DEDENT>else:<EOL><INDENT>b[i] = np.sinc(n[i]/Ns)*np.cos(np.pi*a*n[i]/Ns)/(<NUM_LIT:1> - <NUM_LIT:4>*(a*n[i]/Ns)**<NUM_LIT:2>)<EOL><DEDENT><DEDENT>return b<EOL>

A truncated raised cosine pulse used in digital communications.

The pulse shaping factor :math:`0 < \\alpha < 1` is required as well as the
truncation factor M which sets the pulse duration to be :math:`2*M*T_{symbol}`.

Parameters
----------
Ns : number of samples per symbol
alpha : excess bandwidth factor on (0, 1), e.g., 0.35
M : equals RC one-sided symbol truncation factor

Returns
-------
b : ndarray containing the pulse shape

See Also
--------
sqrt_rc_imp

Notes
-----
The pulse shape b is typically used as the FIR filter coefficients
when forming a pulse shaped digital communications waveform.

Examples
--------
Ten samples per symbol and :math:`\\alpha = 0.35`.

>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm.digitalcom import rc_imp
>>> from numpy import arange
>>> b = rc_imp(10,0.35)
>>> n = arange(-10*6,10*6+1)
>>> plt.stem(n,b)
>>> plt.show()


<EOL>n = np.arange(-M*Ns,M*Ns+<NUM_LIT:1>)<EOL>b = np.zeros(len(n))<EOL>Ns *= <NUM_LIT:1.0><EOL>a = alpha<EOL>for i in range(len(n)):<EOL><INDENT>if abs(<NUM_LIT:1> - <NUM_LIT:16>*a**<NUM_LIT:2>*(n[i]/Ns)**<NUM_LIT:2>) <= np.finfo(np.float).eps/<NUM_LIT:2>:<EOL><INDENT>b[i] = <NUM_LIT:1>/<NUM_LIT>*((<NUM_LIT:1>+a)*np.sin((<NUM_LIT:1>+a)*np.pi/(<NUM_LIT>*a))-(<NUM_LIT:1>-a)*np.cos((<NUM_LIT:1>-a)*np.pi/(<NUM_LIT>*a))+(<NUM_LIT:4>*a)/np.pi*np.sin((<NUM_LIT:1>-a)*np.pi/(<NUM_LIT>*a)))<EOL><DEDENT>else:<EOL><INDENT>b[i] = <NUM_LIT:4>*a/(np.pi*(<NUM_LIT:1> - <NUM_LIT:16>*a**<NUM_LIT:2>*(n[i]/Ns)**<NUM_LIT:2>))<EOL>b[i] = b[i]*(np.cos((<NUM_LIT:1>+a)*np.pi*n[i]/Ns) + np.sinc((<NUM_LIT:1>-a)*n[i]/Ns)*(<NUM_LIT:1>-a)*np.pi/(<NUM_LIT>*a))<EOL><DEDENT><DEDENT>return b<EOL>

A truncated square root raised cosine pulse used in digital communications.

The pulse shaping factor :math:`0 < \\alpha < 1` is required as well as the
truncation factor M which sets the pulse duration to be :math:`2*M*T_{symbol}`.

Parameters
----------
Ns : number of samples per symbol
alpha : excess bandwidth factor on (0, 1), e.g., 0.35
M : equals RC one-sided symbol truncation factor

Returns
-------
b : ndarray containing the pulse shape

Notes
-----
The pulse shape b is typically used as the FIR filter coefficients
when forming a pulse shaped digital communications waveform. When
square root raised cosine (SRC) pulse is used to generate Tx signals and
at the receiver used as a matched filter (receiver FIR filter), the
received signal is now raised cosine shaped, thus having zero
intersymbol interference and the optimum removal of additive white
noise if present at the receiver input.

Examples
--------
Ten samples per symbol and :math:`\\alpha = 0.35`.

>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.digitalcom import sqrt_rc_imp
>>> b = sqrt_rc_imp(10,0.35)
>>> n = arange(-10*6,10*6+1)
>>> plt.stem(n,b)
>>> plt.show()


def RZ_bits(N_bits,Ns,pulse='<STR_LIT>',alpha = <NUM_LIT>,M=<NUM_LIT:6>):

data = np.random.randint(<NUM_LIT:0>,<NUM_LIT:2>,N_bits) <EOL>x = np.hstack((data.reshape(N_bits,<NUM_LIT:1>),np.zeros((N_bits,int(Ns)-<NUM_LIT:1>))))<EOL>x =x.flatten()<EOL>if pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = np.ones(int(Ns))<EOL><DEDENT>elif pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = rc_imp(Ns,alpha,M)<EOL><DEDENT>elif pulse.lower() == '<STR_LIT:src>':<EOL><INDENT>b = sqrt_rc_imp(Ns,alpha,M)<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>x = signal.lfilter(b,<NUM_LIT:1>,x)<EOL>return x,b/float(Ns),data<EOL>

Generate return-to-zero (RZ) data bits with pulse shaping.

A baseband digital data signal using +/-1 amplitude signal values
and including pulse shaping.

Parameters
----------
N_bits : number of RZ {0,1} data bits to produce
Ns : the number of samples per bit,
pulse_type : 'rect' , 'rc', 'src' (default 'rect')
alpha : excess bandwidth factor(default 0.25)
M : single sided pulse duration (default = 6)

Returns
-------
x : ndarray of the RZ signal values
b : ndarray of the pulse shape
data : ndarray of the underlying data bits

Notes
-----
Pulse shapes include 'rect' (rectangular), 'rc' (raised cosine),
'src' (root raised cosine). The actual pulse length is 2*M+1 samples.
This function is used by BPSK_tx in the Case Study article.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.digitalcom import RZ_bits
>>> x,b,data = RZ_bits(100,10)
>>> t = arange(len(x))
>>> plt.plot(t,x)
>>> plt.ylim([-0.01, 1.01])
>>> plt.show()


Px,f = pylab.mlab.psd(x,NFFT,Fs)<EOL>return Px.flatten(), f<EOL>

A local version of NumPy's PSD function that returns the plot arrays.

A mlab.psd wrapper function that returns two ndarrays;
makes no attempt to auto plot anything.

Parameters
----------
x : ndarray input signal
NFFT : a power of two, e.g., 2**10 = 1024
Fs : the sampling rate in Hz

Returns
-------
Px : ndarray of the power spectrum estimate
f : ndarray of frequency values

Notes
-----
This function makes it easier to overlay spectrum plots because
you have better control over the axis scaling than when using psd()
in the autoscale mode.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm import digitalcom as dc
>>> from numpy import log10
>>> x,b, data = dc.NRZ_bits(10000,10)
>>> Px,f = dc.my_psd(x,2**10,10)
>>> plt.plot(f, 10*log10(Px))
>>> plt.show()


if type(D) == float or type(D) == int:<EOL><INDENT>if int(np.fix(D)) < <NUM_LIT:1>:<EOL><INDENT>print('<STR_LIT>')<EOL>exit(<NUM_LIT:1>)<EOL><DEDENT>if int(np.fix(D)) > N-<NUM_LIT:2>:<EOL><INDENT>print('<STR_LIT>')<EOL>exit(<NUM_LIT:1>)<EOL><DEDENT>D_frac = D - np.fix(D)<EOL>Nd = int(np.fix(D))<EOL>b = np.zeros(Nd + <NUM_LIT:4>)<EOL>b[Nd] = -(D_frac-<NUM_LIT:1>)*(D_frac-<NUM_LIT:2>)*(D_frac-<NUM_LIT:3>)/<NUM_LIT><EOL>b[Nd + <NUM_LIT:1>] = D_frac*(D_frac-<NUM_LIT:2>)*(D_frac-<NUM_LIT:3>)/<NUM_LIT><EOL>b[Nd + <NUM_LIT:2>] = -D_frac*(D_frac-<NUM_LIT:1>)*(D_frac-<NUM_LIT:3>)/<NUM_LIT><EOL>b[Nd + <NUM_LIT:3>] = D_frac*(D_frac-<NUM_LIT:1>)*(D_frac-<NUM_LIT:2>)/<NUM_LIT><EOL>y = signal.lfilter(b,[<NUM_LIT:1>],x)<EOL><DEDENT>else:<EOL><INDENT>if np.fix(np.min(D)) < <NUM_LIT:1>:<EOL><INDENT>print('<STR_LIT>')<EOL>exit(<NUM_LIT:1>)<EOL><DEDENT>if np.fix(np.max(D)) > N-<NUM_LIT:2>:<EOL><INDENT>print('<STR_LIT>')<EOL>exit(<NUM_LIT:1>)<EOL><DEDENT>y = np.zeros(len(x))<EOL>X = np.zeros(N+<NUM_LIT:1>)<EOL>W3 = np.array([[<NUM_LIT:1.>/<NUM_LIT:6>, -<NUM_LIT:1.>/<NUM_LIT:2>, <NUM_LIT:1.>/<NUM_LIT:2>, -<NUM_LIT:1.>/<NUM_LIT:6>]])<EOL>W2 = np.array([[<NUM_LIT:0>, <NUM_LIT:1.>/<NUM_LIT:2>, -<NUM_LIT:1.>, <NUM_LIT:1.>/<NUM_LIT:2>]])<EOL>W1 = np.array([[-<NUM_LIT:1.>/<NUM_LIT:6>, <NUM_LIT:1.>, -<NUM_LIT:1.>/<NUM_LIT:2>, -<NUM_LIT:1.>/<NUM_LIT:3>]])<EOL>W0 = np.array([[<NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1.>, <NUM_LIT:0>]])<EOL>for k in range(len(x)):<EOL><INDENT>Nd = int(np.fix(D[k]))<EOL>mu = <NUM_LIT:1> - (D[k]-np.fix(D[k]))<EOL>X = np.hstack((np.array(x[k]), X[:-<NUM_LIT:1>]))<EOL>v3 = np.dot(W3,np.array(X[Nd-<NUM_LIT:1>:Nd+<NUM_LIT:3>]).T)<EOL>v2 = np.dot(W2,np.array(X[Nd-<NUM_LIT:1>:Nd+<NUM_LIT:3>]).T)<EOL>v1 = np.dot(W1,np.array(X[Nd-<NUM_LIT:1>:Nd+<NUM_LIT:3>]).T)<EOL>v0 = np.dot(W0,np.array(X[Nd-<NUM_LIT:1>:Nd+<NUM_LIT:3>]).T)<EOL>y[k] = ((v3[<NUM_LIT:0>]*mu + v2[<NUM_LIT:0>])*mu + v1[<NUM_LIT:0>])*mu + v0[<NUM_LIT:0>]<EOL><DEDENT><DEDENT>return y<EOL>

A time varying time delay which takes advantage of the Farrow structure
for cubic interpolation:

y = time_delay(x,D,N = 3)

Note that D is an array of the same length as the input signal x. This
allows you to make the delay a function of time. If you want a constant
delay just use D*zeros(len(x)). The minimum delay allowable is one sample
or D = 1.0. This is due to the causal system nature of the Farrow
structure.

A founding paper on the subject of interpolators is: C. W. Farrow, "A
Continuously variable Digital Delay Element," Proceedings of the IEEE
Intern. Symp. on Circuits Syst., pp. 2641-2645, June 1988.

Mark Wickert, February 2014


K = <NUM_LIT:2>*(int(np.floor(len(x1)/<NUM_LIT:2>)))<EOL>X1 = fft.fft(x1[:K])<EOL>X2 = fft.fft(x2[:K])<EOL>E1 = sum(abs(x1[:K])**<NUM_LIT:2>)<EOL>E2 = sum(abs(x2[:K])**<NUM_LIT:2>)<EOL>r12 = np.fft.ifft(X1*np.conj(X2))/np.sqrt(E1*E2)<EOL>k = np.arange(K) - int(np.floor(K/<NUM_LIT:2>))<EOL>r12 = np.fft.fftshift(r12)<EOL>idx = np.nonzero(np.ravel(abs(k) <= Nlags))<EOL>return r12[idx], k[idx]<EOL>

r12, k = xcorr(x1,x2,Nlags), r12 and k are ndarray's
Compute the energy normalized cross correlation between the sequences
x1 and x2. If x1 = x2 the cross correlation is the autocorrelation.
The number of lags sets how many lags to return centered about zero


return <NUM_LIT:1.>/<NUM_LIT:2>*erfc(x/np.sqrt(<NUM_LIT>))<EOL>


xq = np.int16(np.rint(x*<NUM_LIT:2>**(N_bits-<NUM_LIT:1>)))<EOL>x_bits = np.zeros((N_bits,len(xq)))<EOL>for k, xk in enumerate(xq):<EOL><INDENT>x_bits[:,k] = to_bin(xk,N_bits)<EOL><DEDENT>x_bits = np.reshape(x_bits,(<NUM_LIT:1>,len(x)*N_bits),'<STR_LIT:F>')<EOL>return np.int16(x_bits.flatten())<EOL>

Parameters
----------
x : signal samples to be PCM encoded
N_bits ; bit precision of PCM samples

Returns
-------
x_bits = encoded serial bit stream of 0/1 values. MSB first.

Mark Wickert, Mark 2015


data_str = bin(data & (<NUM_LIT:2>**width-<NUM_LIT:1>))[<NUM_LIT:2>:].zfill(width)<EOL>return [int(x) for x in tuple(data_str)]<EOL>

Convert an unsigned integer to a numpy binary array with the first
element the MSB and the last element the LSB.


width = len(bin_array)<EOL>bin_wgts = <NUM_LIT:2>**np.arange(width-<NUM_LIT:1>,-<NUM_LIT:1>,-<NUM_LIT:1>)<EOL>return int(np.dot(bin_array,bin_wgts))<EOL>

Convert binary array back a nonnegative integer. The array length is
the bit width. The first input index holds the MSB and the last holds the LSB.


N_samples = len(x_bits)//N_bits<EOL>xrs_bits = x_bits.copy()<EOL>xrs_bits = np.reshape(xrs_bits,(N_bits,N_samples),'<STR_LIT:F>')<EOL>xq = np.zeros(N_samples)<EOL>w = <NUM_LIT:2>**np.arange(N_bits-<NUM_LIT:1>,-<NUM_LIT:1>,-<NUM_LIT:1>) <EOL>for k in range(N_samples):<EOL><INDENT>xq[k] = np.dot(xrs_bits[:,k],w) - xrs_bits[<NUM_LIT:0>,k]*<NUM_LIT:2>**N_bits<EOL><DEDENT>return xq/<NUM_LIT:2>**(N_bits-<NUM_LIT:1>)<EOL>

Parameters
----------
x_bits : serial bit stream of 0/1 values. The length of
         x_bits must be a multiple of N_bits
N_bits : bit precision of PCM samples

Returns
-------
  xhat : decoded PCM signal samples

Mark Wickert, March 2015


x_bits = <NUM_LIT:2>*x_bits - <NUM_LIT:1> <EOL>var_noise = <NUM_LIT:10>**(-EBN0_dB/<NUM_LIT:10>)/<NUM_LIT:2><EOL>y_bits = x_bits + np.sqrt(var_noise)*np.random.randn(np.size(x_bits))<EOL>y_bits = np.sign(y_bits) <EOL>y_bits = (y_bits+<NUM_LIT:1>)/<NUM_LIT:2> <EOL>return y_bits<EOL>

Parameters
----------
x_bits : serial bit stream of 0/1 values.
EBN0_dB : Energy per bit to noise power density ratio in dB of the serial bit stream sent through the AWGN channel. Frequently we equate EBN0 to SNR in link budget calculations.

Returns
-------
y_bits : Received serial bit stream following hard decisions. This bit will have bit errors. To check the estimated bit error probability use :func:`BPSK_BEP` or simply:
>>> Pe_est = sum(xor(x_bits,y_bits))/length(x_bits);

Mark Wickert, March 2015


N_OFDM = IQ_data.shape[<NUM_LIT:0>]<EOL>Npb = N_OFDM // (Np - <NUM_LIT:1>)<EOL>N_OFDM_rem = N_OFDM - Npb * (Np - <NUM_LIT:1>)<EOL>Nf = IQ_data.shape[<NUM_LIT:1>]<EOL>IQ_datap = np.zeros((N_OFDM + Npb + <NUM_LIT:1>, Nf), dtype=np.complex128)<EOL>pilots = np.ones(Nf)  <EOL>for k in range(Npb):<EOL><INDENT>IQ_datap[Np * k:Np * (k + <NUM_LIT:1>), :] = np.vstack((pilots,<EOL>IQ_data[(Np - <NUM_LIT:1>) * k:(Np - <NUM_LIT:1>) * (k + <NUM_LIT:1>), :]))<EOL><DEDENT>IQ_datap[Np * Npb:Np * (Npb + N_OFDM_rem), :] = np.vstack((pilots,<EOL>IQ_data[(Np - <NUM_LIT:1>) * Npb:, :]))<EOL>return IQ_datap<EOL>

Parameters
----------
IQ_data : a 2D array of input QAM symbols with the columns
           representing the NF carrier frequencies and each
           row the QAM symbols used to form an OFDM symbol
Np : the period of the pilot blocks; e.g., a pilot block is
           inserted every Np OFDM symbols (Np-1 OFDM data symbols
           of width Nf are inserted in between the pilot blocks.

Returns
-------
IQ_datap : IQ_data with pilot blocks inserted

See Also
--------
OFDM_tx

Notes
-----
A helper function called by :func:`OFDM_tx` that inserts pilot block for use
in channel estimation when a delay spread channel is present.


def OFDM_tx(IQ_data, Nf, N, Np=<NUM_LIT:0>, cp=False, Ncp=<NUM_LIT:0>):

N_symb = len(IQ_data)<EOL>N_OFDM = N_symb // Nf<EOL>IQ_data = IQ_data[:N_OFDM * Nf]<EOL>IQ_s2p = np.reshape(IQ_data, (N_OFDM, Nf))  <EOL>print(IQ_s2p.shape)<EOL>if Np > <NUM_LIT:0>:<EOL><INDENT>IQ_s2p = mux_pilot_blocks(IQ_s2p, Np)<EOL>N_OFDM = IQ_s2p.shape[<NUM_LIT:0>]<EOL>print(IQ_s2p.shape)<EOL><DEDENT>if cp:<EOL><INDENT>x_out = np.zeros(N_OFDM * (N + Ncp), dtype=np.complex128)<EOL><DEDENT>else:<EOL><INDENT>x_out = np.zeros(N_OFDM * N, dtype=np.complex128)<EOL><DEDENT>for k in range(N_OFDM):<EOL><INDENT>buff = np.zeros(N, dtype=np.complex128)<EOL>for n in range(-Nf // <NUM_LIT:2>, Nf // <NUM_LIT:2> + <NUM_LIT:1>):<EOL><INDENT>if n == <NUM_LIT:0>:  <EOL><INDENT>buff[<NUM_LIT:0>] = <NUM_LIT:0>  <EOL><DEDENT>elif n > <NUM_LIT:0>:  <EOL><INDENT>buff[n] = IQ_s2p[k, n - <NUM_LIT:1>]<EOL><DEDENT>else:  <EOL><INDENT>buff[N + n] = IQ_s2p[k, Nf + n]<EOL><DEDENT><DEDENT>if cp:<EOL><INDENT>x_out_buff = fft.ifft(buff)<EOL>x_out[k * (N + Ncp):(k + <NUM_LIT:1>) * (N + Ncp)] = np.concatenate((x_out_buff[N - Ncp:],<EOL>x_out_buff))<EOL><DEDENT>else:<EOL><INDENT>x_out[k * N:(k + <NUM_LIT:1>) * N] = fft.ifft(buff)<EOL><DEDENT><DEDENT>return x_out<EOL>

Parameters
----------
IQ_data : +/-1, +/-3, etc complex QAM symbol sample inputs
Nf : number of filled carriers, must be even and Nf < N
N : total number of carriers; generally a power 2, e.g., 64, 1024, etc
Np : Period of pilot code blocks; 0 <=> no pilots
cp : False/True <=> bypass cp insertion entirely if False
Ncp : the length of the cyclic prefix

Returns
-------
x_out : complex baseband OFDM waveform output after P/S and CP insertion

See Also
--------
OFDM_rx

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm import digitalcom as dc
>>> x1,b1,IQ_data1 = dc.QAM_bb(50000,1,'16qam')
>>> x_out = dc.OFDM_tx(IQ_data1,32,64)
>>> plt.psd(x_out,2**10,1);
>>> plt.xlabel(r'Normalized Frequency ($\omega/(2\pi)=f/f_s$)')
>>> plt.ylim([-40,0])
>>> plt.xlim([-.5,.5])
>>> plt.show()


def chan_est_equalize(z, Np, alpha, Ht=None):

N_OFDM = z.shape[<NUM_LIT:0>]<EOL>Nf = z.shape[<NUM_LIT:1>]<EOL>Npb = N_OFDM // Np<EOL>N_part = N_OFDM - Npb * Np - <NUM_LIT:1><EOL>zz_out = np.zeros_like(z)<EOL>Hmatrix = np.zeros((N_OFDM, Nf), dtype=np.complex128)<EOL>k_fill = <NUM_LIT:0><EOL>k_pilot = <NUM_LIT:0><EOL>for k in range(N_OFDM):<EOL><INDENT>if np.mod(k, Np) == <NUM_LIT:0>:  <EOL><INDENT>if k == <NUM_LIT:0>:<EOL><INDENT>H = z[k, :]<EOL><DEDENT>else:<EOL><INDENT>H = alpha * H + (<NUM_LIT:1> - alpha) * z[k, :]<EOL><DEDENT>Hmatrix[k_pilot, :] = H<EOL>k_pilot += <NUM_LIT:1><EOL><DEDENT>else:  <EOL><INDENT>if isinstance(type(None), type(Ht)):<EOL><INDENT>zz_out[k_fill, :] = z[k, :] / H  <EOL><DEDENT>else:<EOL><INDENT>zz_out[k_fill, :] = z[k, :] / Ht  <EOL><DEDENT>k_fill += <NUM_LIT:1><EOL><DEDENT><DEDENT>zz_out = zz_out[:k_fill, :]  <EOL>Hmatrix = Hmatrix[:k_pilot, :]  <EOL>if k_pilot > <NUM_LIT:0>:  <EOL><INDENT>chan_idx = np.arange(<NUM_LIT:0>, Nf // <NUM_LIT:2>, <NUM_LIT:4>)<EOL>plt.subplot(<NUM_LIT>)<EOL>for i in chan_idx:<EOL><INDENT>plt.plot(np.abs(Hmatrix[:, i]))<EOL><DEDENT>plt.title('<STR_LIT>')<EOL>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.grid();<EOL>plt.subplot(<NUM_LIT>)<EOL>for i in chan_idx:<EOL><INDENT>plt.plot(np.angle(Hmatrix[:, i]))<EOL><DEDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.grid();<EOL>plt.tight_layout()<EOL><DEDENT>return zz_out, H<EOL>

This is a helper function for :func:`OFDM_rx` to unpack pilot blocks from
from the entire set of received OFDM symbols (the Nf of N filled
carriers only); then estimate the channel array H recursively,
and finally apply H_hat to Y, i.e., X_hat = Y/H_hat
carrier-by-carrier. Note if Np = -1, then H_hat = H, the true
channel.

Parameters
----------
z : Input N_OFDM x Nf 2D array containing pilot blocks and OFDM data symbols.
Np : The pilot block period; if -1 use the known channel impulse response input to ht.
alpha : The forgetting factor used to recursively estimate H_hat
Ht : The theoretical channel frquency response to allow ideal equalization provided Ncp is adequate.

Returns
-------
zz_out : The input z with the pilot blocks removed and one-tap equalization applied to each of the Nf carriers.
H : The channel estimate in the frequency domain; an array of length Nf; will return Ht if provided as an input.

Examples
--------
>>> from sk_dsp_comm.digitalcom import chan_est_equalize
>>> zz_out,H = chan_est_eq(z,Nf,Np,alpha,Ht=None)


def OFDM_rx(x, Nf, N, Np=<NUM_LIT:0>, cp=False, Ncp=<NUM_LIT:0>, alpha=<NUM_LIT>, ht=None):

N_symb = len(x) // (N + Ncp)<EOL>y_out = np.zeros(N_symb * N, dtype=np.complex128)<EOL>for k in range(N_symb):<EOL><INDENT>if cp:<EOL><INDENT>buff = x[k * (N + Ncp) + Ncp:(k + <NUM_LIT:1>) * (N + Ncp)]<EOL><DEDENT>else:<EOL><INDENT>buff = x[k * N:(k + <NUM_LIT:1>) * N]<EOL><DEDENT>y_out[k * N:(k + <NUM_LIT:1>) * N] = fft.fft(buff)<EOL><DEDENT>z_out = np.reshape(y_out, (N_symb, N))<EOL>z_out = np.hstack((z_out[:, <NUM_LIT:1>:Nf // <NUM_LIT:2> + <NUM_LIT:1>], z_out[:, N - Nf // <NUM_LIT:2>:N]))<EOL>if Np > <NUM_LIT:0>:<EOL><INDENT>if isinstance(type(None), type(ht)):<EOL><INDENT>z_out, H = chan_est_equalize(z_out, Np, alpha)<EOL><DEDENT>else:<EOL><INDENT>Ht = fft.fft(ht, N)<EOL>Hht = np.hstack((Ht[<NUM_LIT:1>:Nf // <NUM_LIT:2> + <NUM_LIT:1>], Ht[N - Nf // <NUM_LIT:2>:]))<EOL>z_out, H = chan_est_equalize(z_out, Np, alpha, Hht)<EOL><DEDENT><DEDENT>elif Np == -<NUM_LIT:1>:  <EOL><INDENT>Ht = fft.fft(ht, N)<EOL>H = np.hstack((Ht[<NUM_LIT:1>:Nf // <NUM_LIT:2> + <NUM_LIT:1>], Ht[N - Nf // <NUM_LIT:2>:]))<EOL>for k in range(N_symb):<EOL><INDENT>z_out[k, :] /= H<EOL><DEDENT><DEDENT>else:<EOL><INDENT>H = np.ones(Nf)<EOL><DEDENT>return z_out.flatten(), H<EOL>

Parameters
----------
x : Received complex baseband OFDM signal
Nf : Number of filled carriers, must be even and Nf < N
N : Total number of carriers; generally a power 2, e.g., 64, 1024, etc
Np : Period of pilot code blocks; 0 <=> no pilots; -1 <=> use the ht impulse response input to equalize the OFDM symbols; note equalization still requires Ncp > 0 to work on a delay spread channel.
cp : False/True <=> if False assume no CP is present
Ncp : The length of the cyclic prefix
alpha : The filter forgetting factor in the channel estimator. Typically alpha is 0.9 to 0.99.
nt : Input the known theoretical channel impulse response

Returns
-------
z_out : Recovered complex baseband QAM symbols as a serial stream; as appropriate channel estimation has been applied.
H : channel estimate (in the frequency domain at each subcarrier)

See Also
--------
OFDM_tx

Examples
--------

>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm import digitalcom as dc
>>> from scipy import signal
>>> from numpy import array
>>> hc = array([1.0, 0.1, -0.05, 0.15, 0.2, 0.05]) # impulse response spanning five symbols
>>> # Quick example using the above channel with no cyclic prefix
>>> x1,b1,IQ_data1 = dc.QAM_bb(50000,1,'16qam')
>>> x_out = dc.OFDM_tx(IQ_data1,32,64,0,True,0)
>>> c_out = signal.lfilter(hc,1,x_out) # Apply channel distortion
>>> r_out = dc.cpx_AWGN(c_out,100,64/32) # Es/N0 = 100 dB
>>> z_out,H = dc.OFDM_rx(r_out,32,64,-1,True,0,alpha=0.95,ht=hc)
>>> plt.plot(z_out[200:].real,z_out[200:].imag,'.')
>>> plt.xlabel('In-Phase')
>>> plt.ylabel('Quadrature')
>>> plt.axis('equal')
>>> plt.grid()
>>> plt.show()

Another example with noise using a 10 symbol cyclic prefix and channel estimation:

>>> x_out = dc.OFDM_tx(IQ_data1,32,64,100,True,10)
>>> c_out = signal.lfilter(hc,1,x_out) # Apply channel distortion
>>> r_out = dc.cpx_AWGN(c_out,25,64/32) # Es/N0 = 25 dB
>>> z_out,H = dc.OFDM_rx(r_out,32,64,100,True,10,alpha=0.95,ht=hc);
>>> plt.figure() # if channel estimation is turned on need this
>>> plt.plot(z_out[-2000:].real,z_out[-2000:].imag,'.') # allow settling time
>>> plt.xlabel('In-Phase')
>>> plt.ylabel('Quadrature')
>>> plt.axis('equal')
>>> plt.grid()
>>> plt.show()


bits_in = to_bin(d_word,b_width)<EOL>bits_out = np.zeros(b_width,dtype=np.int)<EOL>for k, bit_k in enumerate(bits_in):<EOL><INDENT>if k > <NUM_LIT:0>:<EOL><INDENT>bits_out[k] = bit_k^bits_in[k-<NUM_LIT:1>]<EOL><DEDENT>else:<EOL><INDENT>bits_out[k] = bit_k<EOL><DEDENT><DEDENT>return from_bin(bits_out)<EOL>

Convert integer bit words to gray encoded binary words via
Gray coding starting from the MSB to the LSB

Mark Wickert November 2018


bits_in = to_bin(d_word,b_width)<EOL>bits_out = np.zeros(b_width,dtype=np.int)<EOL>for k, bit_k in enumerate(bits_in):<EOL><INDENT>if k > <NUM_LIT:0>:<EOL><INDENT>bits_out[k] = bit_k^bits_out[k-<NUM_LIT:1>]<EOL><DEDENT>else:<EOL><INDENT>bits_out[k] = bit_k<EOL><DEDENT><DEDENT>return from_bin(bits_out)<EOL>

Convert gray encoded binary words to integer bit words via
Gray decoding starting from the MSB to the LSB

Mark Wickert November 2018


<EOL>bin2gray1 = [<NUM_LIT:0>,<NUM_LIT:1>]<EOL>bin2gray2 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>]<EOL>bin2gray3 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>,<NUM_LIT:7>,<NUM_LIT:6>,<NUM_LIT:4>,<NUM_LIT:5>] <EOL>bin2gray4 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>,<NUM_LIT:7>,<NUM_LIT:6>,<NUM_LIT:4>,<NUM_LIT:5>,<NUM_LIT:15>,<NUM_LIT>,<NUM_LIT:12>,<NUM_LIT>,<NUM_LIT:8>,<NUM_LIT:9>,<NUM_LIT:11>,<NUM_LIT:10>]<EOL>x_m = np.sqrt(M)-<NUM_LIT:1><EOL>if N_symb == None:<EOL><INDENT>N_symb = int(np.floor(len(ext_data)/np.log2(M)))<EOL>data = ext_data[:N_symb*int(np.log2(M))]<EOL><DEDENT>else:<EOL><INDENT>data = np.random.randint(<NUM_LIT:0>,<NUM_LIT:2>,size=int(np.log2(M))*N_symb)<EOL><DEDENT>x_IQ = np.zeros(N_symb,dtype=np.complex128)<EOL>N_word = int(np.log2(M)/<NUM_LIT:2>)<EOL>w = <NUM_LIT:2>**np.arange(N_word-<NUM_LIT:1>,-<NUM_LIT:1>,-<NUM_LIT:1>)<EOL>if M == <NUM_LIT:2>: <EOL><INDENT>x_IQ = <NUM_LIT:2>*data - <NUM_LIT:1><EOL>x_m = <NUM_LIT:1><EOL><DEDENT>elif M == <NUM_LIT:4>: <EOL><INDENT>for k in range(N_symb):<EOL><INDENT>wordI = data[<NUM_LIT:2>*k*N_word:(<NUM_LIT:2>*k+<NUM_LIT:1>)*N_word]<EOL>wordQ = data[<NUM_LIT:2>*k*N_word+N_word:(<NUM_LIT:2>*k+<NUM_LIT:1>)*N_word+N_word]<EOL>x_IQ[k] = (<NUM_LIT:2>*bin2gray1[np.dot(wordI,w)] - x_m) +<NUM_LIT>*(<NUM_LIT:2>*bin2gray1[np.dot(wordQ,w)] - x_m)<EOL><DEDENT><DEDENT>elif M == <NUM_LIT:16>:<EOL><INDENT>for k in range(N_symb):<EOL><INDENT>wordI = data[<NUM_LIT:2>*k*N_word:(<NUM_LIT:2>*k+<NUM_LIT:1>)*N_word]<EOL>wordQ = data[<NUM_LIT:2>*k*N_word+N_word:(<NUM_LIT:2>*k+<NUM_LIT:1>)*N_word+N_word]<EOL>x_IQ[k] = (<NUM_LIT:2>*bin2gray2[np.dot(wordI,w)] - x_m) +<NUM_LIT>*(<NUM_LIT:2>*bin2gray2[np.dot(wordQ,w)] - x_m)<EOL><DEDENT><DEDENT>elif M == <NUM_LIT:64>:<EOL><INDENT>for k in range(N_symb):<EOL><INDENT>wordI = data[<NUM_LIT:2>*k*N_word:(<NUM_LIT:2>*k+<NUM_LIT:1>)*N_word]<EOL>wordQ = data[<NUM_LIT:2>*k*N_word+N_word:(<NUM_LIT:2>*k+<NUM_LIT:1>)*N_word+N_word]<EOL>x_IQ[k] = (<NUM_LIT:2>*bin2gray3[np.dot(wordI,w)] - x_m) +<NUM_LIT>*(<NUM_LIT:2>*bin2gray3[np.dot(wordQ,w)] - x_m)<EOL><DEDENT><DEDENT>elif M == <NUM_LIT>:<EOL><INDENT>for k in range(N_symb):<EOL><INDENT>wordI = data[<NUM_LIT:2>*k*N_word:(<NUM_LIT:2>*k+<NUM_LIT:1>)*N_word]<EOL>wordQ = data[<NUM_LIT:2>*k*N_word+N_word:(<NUM_LIT:2>*k+<NUM_LIT:1>)*N_word+N_word]<EOL>x_IQ[k] = (<NUM_LIT:2>*bin2gray4[np.dot(wordI,w)] - x_m) +<NUM_LIT>*(<NUM_LIT:2>*bin2gray4[np.dot(wordQ,w)] - x_m)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')        <EOL><DEDENT>if Ns > <NUM_LIT:1>:<EOL><INDENT>if pulse.lower() == '<STR_LIT:src>':<EOL><INDENT>b = sqrt_rc_imp(Ns,alpha,<NUM_LIT:6>)<EOL><DEDENT>elif pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = rc_imp(Ns,alpha,<NUM_LIT:6>)    <EOL><DEDENT>elif pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = np.ones(int(Ns)) <EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>x = signal.lfilter(b,<NUM_LIT:1>,upsample(x_IQ,Ns))<EOL>b = b/sum(b)<EOL>return x/x_m, b, data<EOL><DEDENT>else:<EOL><INDENT>return x_IQ/x_m, <NUM_LIT:1>, data<EOL><DEDENT>

QAM_gray_bb: A gray code mapped QAM complex baseband transmitter
x,b,tx_data = QAM_gray_bb(K,Ns,M)

Parameters
----------
N_symb : The number of symbols to process
Ns : Number of samples per symbol
M : Modulation order: 2, 4, 16, 64, 256 QAM. Note 2 <=> BPSK, 4 <=> QPSK
alpha : Square root raised cosine excess bandwidth factor.
        For DOCSIS alpha = 0.12 to 0.18. In general alpha can range over 0 < alpha < 1.
pulse : 'rect', 'src', or 'rc'

Returns
-------
x : Complex baseband digital modulation
b : Transmitter shaping filter, rectangle or SRC
tx_data : xI+1j*xQ = inphase symbol sequence + 1j*quadrature symbol sequence

See Also
--------
QAM_gray_decode

Examples
--------


def QAM_gray_decode(x_hat,M = <NUM_LIT:4>):

<EOL>gray2bin1 = [<NUM_LIT:0>,<NUM_LIT:1>]<EOL>gray2bin2 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>]<EOL>gray2bin3 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>,<NUM_LIT:6>,<NUM_LIT:7>,<NUM_LIT:5>,<NUM_LIT:4>] <EOL>gray2bin4 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>,<NUM_LIT:6>,<NUM_LIT:7>,<NUM_LIT:5>,<NUM_LIT:4>,<NUM_LIT:12>,<NUM_LIT>,<NUM_LIT:15>,<NUM_LIT>,<NUM_LIT:10>,<NUM_LIT:11>,<NUM_LIT:9>,<NUM_LIT:8>]<EOL>x_m = np.sqrt(M)-<NUM_LIT:1><EOL>if M == <NUM_LIT:2>: x_m = <NUM_LIT:1><EOL>N_symb = len(x_hat)<EOL>N_word = int(np.log2(M)/<NUM_LIT:2>)<EOL>x_hat = x_hat/(np.std(x_hat) * np.sqrt(<NUM_LIT:3>/(<NUM_LIT:2>*(M-<NUM_LIT:1>))))<EOL>k_hat_gray = (x_hat + x_m*(<NUM_LIT:1>+<NUM_LIT>))/<NUM_LIT:2><EOL>k_hat_grayI = np.int16(np.clip(np.rint(k_hat_gray.real),<NUM_LIT:0>,x_m))<EOL>k_hat_grayQ = np.int16(np.clip(np.rint(k_hat_gray.imag),<NUM_LIT:0>,x_m))<EOL>data_hat = np.zeros(<NUM_LIT:2>*N_word*N_symb,dtype=int)<EOL>for k in range(N_symb):<EOL><INDENT>if M == <NUM_LIT:2>: <EOL><INDENT>data_hat = k_hat_grayI<EOL><DEDENT>elif M == <NUM_LIT:4>: <EOL><INDENT>data_hat[<NUM_LIT:2>*k*N_word:<NUM_LIT:2>*(k+<NUM_LIT:1>)*N_word]= np.hstack((to_bin(gray2bin1[k_hat_grayI[k]],N_word),<EOL>to_bin(gray2bin1[k_hat_grayQ[k]],N_word)))<EOL><DEDENT>elif M == <NUM_LIT:16>:<EOL><INDENT>data_hat[<NUM_LIT:2>*k*N_word:<NUM_LIT:2>*(k+<NUM_LIT:1>)*N_word]= np.hstack((to_bin(gray2bin2[k_hat_grayI[k]],N_word),<EOL>to_bin(gray2bin2[k_hat_grayQ[k]],N_word)))            <EOL><DEDENT>elif M == <NUM_LIT:64>:<EOL><INDENT>data_hat[<NUM_LIT:2>*k*N_word:<NUM_LIT:2>*(k+<NUM_LIT:1>)*N_word]= np.hstack((to_bin(gray2bin3[k_hat_grayI[k]],N_word),<EOL>to_bin(gray2bin3[k_hat_grayQ[k]],N_word)))            <EOL><DEDENT>elif M == <NUM_LIT>:<EOL><INDENT>data_hat[<NUM_LIT:2>*k*N_word:<NUM_LIT:2>*(k+<NUM_LIT:1>)*N_word]= np.hstack((to_bin(gray2bin4[k_hat_grayI[k]],N_word),<EOL>to_bin(gray2bin4[k_hat_grayQ[k]],N_word)))<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')  <EOL><DEDENT><DEDENT>return data_hat<EOL>

Decode MQAM IQ symbols to a serial bit stream using
gray2bin decoding

x_hat = symbol spaced samples of the QAM waveform taken at the maximum
        eye opening. Normally this is following the matched filter

Mark Wickert April 2018


<EOL>bin2gray1 = [<NUM_LIT:0>,<NUM_LIT:1>]<EOL>bin2gray2 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>]<EOL>bin2gray3 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>,<NUM_LIT:7>,<NUM_LIT:6>,<NUM_LIT:4>,<NUM_LIT:5>] <EOL>bin2gray4 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>,<NUM_LIT:7>,<NUM_LIT:6>,<NUM_LIT:4>,<NUM_LIT:5>,<NUM_LIT:15>,<NUM_LIT>,<NUM_LIT:12>,<NUM_LIT>,<NUM_LIT:8>,<NUM_LIT:9>,<NUM_LIT:11>,<NUM_LIT:10>]<EOL>bin2gray5 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>,<NUM_LIT:7>,<NUM_LIT:6>,<NUM_LIT:4>,<NUM_LIT:5>,<NUM_LIT:15>,<NUM_LIT>,<NUM_LIT:12>,<NUM_LIT>,<NUM_LIT:8>,<NUM_LIT:9>,<NUM_LIT:11>,<NUM_LIT:10>,<NUM_LIT>,<NUM_LIT:30>,<EOL><NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT:16>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT:20>,<NUM_LIT>]<EOL>N_word = int(np.log2(M))<EOL>if N_symb == None:<EOL><INDENT>N_symb = int(np.floor(len(ext_data)/N_word))<EOL>data = ext_data[:N_symb*N_word]<EOL><DEDENT>else:<EOL><INDENT>data = np.random.randint(<NUM_LIT:0>,<NUM_LIT:2>,size=int(np.log2(M))*N_symb)<EOL><DEDENT>x_IQ = np.zeros(N_symb,dtype=np.complex128)<EOL>bin_wgts = <NUM_LIT:2>**np.arange(N_word-<NUM_LIT:1>,-<NUM_LIT:1>,-<NUM_LIT:1>)<EOL>if M == <NUM_LIT:2>: <EOL><INDENT>x_IQ = <NUM_LIT:2>*data - <NUM_LIT:1><EOL><DEDENT>elif M == <NUM_LIT:4>: <EOL><INDENT>for k in range(N_symb):<EOL><INDENT>word_phase = data[k*N_word:(k+<NUM_LIT:1>)*N_word]<EOL>x_phase = <NUM_LIT:2>*np.pi*bin2gray2[np.dot(word_phase,bin_wgts)]/M + np.pi/M<EOL>x_IQ[k] = np.exp(<NUM_LIT>*x_phase)<EOL><DEDENT><DEDENT>elif M == <NUM_LIT:8>:<EOL><INDENT>for k in range(N_symb):<EOL><INDENT>word_phase = data[k*N_word:(k+<NUM_LIT:1>)*N_word]<EOL>x_phase = <NUM_LIT:2>*np.pi*bin2gray3[np.dot(word_phase,bin_wgts)]/M<EOL>x_IQ[k] = np.exp(<NUM_LIT>*x_phase)<EOL><DEDENT><DEDENT>elif M == <NUM_LIT:16>:<EOL><INDENT>for k in range(N_symb):<EOL><INDENT>word_phase = data[k*N_word:(k+<NUM_LIT:1>)*N_word]<EOL>x_phase = <NUM_LIT:2>*np.pi*bin2gray4[np.dot(word_phase,bin_wgts)]/M<EOL>x_IQ[k] = np.exp(<NUM_LIT>*x_phase)<EOL><DEDENT><DEDENT>elif M == <NUM_LIT:32>:<EOL><INDENT>for k in range(N_symb):<EOL><INDENT>word_phase = data[k*N_word:(k+<NUM_LIT:1>)*N_word]<EOL>x_phase = <NUM_LIT:2>*np.pi*bin2gray5[np.dot(word_phase,bin_wgts)]/M<EOL>x_IQ[k] = np.exp(<NUM_LIT>*x_phase)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')        <EOL><DEDENT>if Ns > <NUM_LIT:1>:<EOL><INDENT>if pulse.lower() == '<STR_LIT:src>':<EOL><INDENT>b = sqrt_rc_imp(Ns,alpha,<NUM_LIT:6>)<EOL><DEDENT>elif pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = rc_imp(Ns,alpha,<NUM_LIT:6>)    <EOL><DEDENT>elif pulse.lower() == '<STR_LIT>':<EOL><INDENT>b = np.ones(int(Ns)) <EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>x = signal.lfilter(b,<NUM_LIT:1>,upsample(x_IQ,Ns))<EOL>b = b/sum(b)<EOL>return x, b, data<EOL><DEDENT>else:<EOL><INDENT>return x_IQ, <NUM_LIT:1>, data<EOL><DEDENT>

MPSK_gray_bb: A gray code mapped MPSK complex baseband transmitter
x,b,tx_data = MPSK_gray_bb(K,Ns,M)

//////////// Inputs //////////////////////////////////////////////////
  N_symb = the number of symbols to process
      Ns = number of samples per symbol
       M = modulation order: 2, 4, 8, 16 MPSK
   alpha = squareroot raised cosine excess bandwidth factor.
           Can range over 0 < alpha < 1.
   pulse = 'rect', 'src', or 'rc'
//////////// Outputs /////////////////////////////////////////////////
       x = complex baseband digital modulation
       b = transmitter shaping filter, rectangle or SRC
 tx_data = xI+1j*xQ = inphase symbol sequence +
           1j*quadrature symbol sequence

Mark Wickert November 2018


def MPSK_gray_decode(x_hat,M = <NUM_LIT:4>):

<EOL>gray2bin1 = [<NUM_LIT:0>,<NUM_LIT:1>]<EOL>gray2bin2 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>]<EOL>gray2bin3 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>,<NUM_LIT:6>,<NUM_LIT:7>,<NUM_LIT:5>,<NUM_LIT:4>] <EOL>gray2bin4 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>,<NUM_LIT:6>,<NUM_LIT:7>,<NUM_LIT:5>,<NUM_LIT:4>,<NUM_LIT:12>,<NUM_LIT>,<NUM_LIT:15>,<NUM_LIT>,<NUM_LIT:10>,<NUM_LIT:11>,<NUM_LIT:9>,<NUM_LIT:8>]<EOL>gray2bin5 = [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:3>,<NUM_LIT:2>,<NUM_LIT:6>,<NUM_LIT:7>,<NUM_LIT:5>,<NUM_LIT:4>,<NUM_LIT:12>,<NUM_LIT>,<NUM_LIT:15>,<NUM_LIT>,<NUM_LIT:10>,<NUM_LIT:11>,<NUM_LIT:9>,<NUM_LIT:8>,<NUM_LIT>,<NUM_LIT>,<EOL><NUM_LIT>,<NUM_LIT>,<NUM_LIT:30>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT:20>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT:16>]<EOL>N_symb = len(x_hat)<EOL>N_word = int(np.log2(M))<EOL>if M == <NUM_LIT:4>:<EOL><INDENT>k_hat_gray_theta = np.mod(np.int64(np.rint(np.angle(x_hat *np.exp(-<NUM_LIT>*np.pi/<NUM_LIT:4>))*M/<NUM_LIT:2>/np.pi)),M)<EOL><DEDENT>else:<EOL><INDENT>k_hat_gray_theta = np.mod((np.rint(np.angle(x_hat)*M/<NUM_LIT:2>/np.pi)).astype(np.int),M)<EOL><DEDENT>data_hat = np.zeros(N_symb*N_word,dtype=int)<EOL>for k in range(N_symb):<EOL><INDENT>if M == <NUM_LIT:2>: <EOL><INDENT>data_hat[k] = k_hat_gray_theta[k]<EOL><DEDENT>elif M == <NUM_LIT:4>: <EOL><INDENT>data_hat[k*N_word:(k+<NUM_LIT:1>)*N_word]= to_bin(gray2bin2[k_hat_gray_theta[k]],N_word)<EOL><DEDENT>elif M == <NUM_LIT:8>:<EOL><INDENT>data_hat[k*N_word:(k+<NUM_LIT:1>)*N_word]= to_bin(gray2bin3[k_hat_gray_theta[k]],N_word)            <EOL><DEDENT>elif M == <NUM_LIT:16>:<EOL><INDENT>data_hat[k*N_word:(k+<NUM_LIT:1>)*N_word]= to_bin(gray2bin4[k_hat_gray_theta[k]],N_word)            <EOL><DEDENT>elif M == <NUM_LIT:32>:<EOL><INDENT>data_hat[k*N_word:(k+<NUM_LIT:1>)*N_word]= to_bin(gray2bin5[k_hat_gray_theta[k]],N_word)<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')  <EOL><DEDENT><DEDENT>return data_hat<EOL>

Decode MPSK IQ symbols to a serial bit stream using
gray2bin decoding

x_hat = symbol spaced samples of the MPSK waveform taken at the maximum
        eye opening. Normally this is following the matched filter

Mark Wickert November 2018


def MPSK_BEP_thy(SNR_dB, M, EbN0_Mode = True):

if EbN0_Mode:<EOL><INDENT>EsN0_dB = SNR_dB + <NUM_LIT:10>*np.log10(np.log2(M))<EOL><DEDENT>else:<EOL><INDENT>EsN0_dB = SNR_dB<EOL><DEDENT>Symb2Bits = np.log2(M)<EOL>if M == <NUM_LIT:2>:<EOL><INDENT>BEP = Q_fctn(np.sqrt(<NUM_LIT:2>*<NUM_LIT:10>**(EsN0_dB/<NUM_LIT:10>)))<EOL><DEDENT>else:<EOL><INDENT>SEP = <NUM_LIT:2>*Q_fctn(np.sqrt(<NUM_LIT:2>*<NUM_LIT:10>**(EsN0_dB/<NUM_LIT:10>))*np.sin(np.pi/M))<EOL>BEP = SEP/Symb2Bits<EOL><DEDENT>return BEP<EOL>

Approximate the bit error probability of MPSK assuming Gray encoding

Mark Wickert November 2018


def QAM_BEP_thy(SNR_dB,M,EbN0_Mode = True):

if EbN0_Mode:<EOL><INDENT>EsN0_dB = SNR_dB + <NUM_LIT:10>*np.log10(np.log2(M))<EOL><DEDENT>else:<EOL><INDENT>EsN0_dB = SNR_dB<EOL><DEDENT>if M == <NUM_LIT:2>:<EOL><INDENT>BEP = Q_fctn(np.sqrt(<NUM_LIT:2>*<NUM_LIT:10>**(EsN0_dB/<NUM_LIT:10>)))<EOL><DEDENT>elif M > <NUM_LIT:2>:<EOL><INDENT>SEP = <NUM_LIT:4>*(<NUM_LIT:1> - <NUM_LIT:1>/np.sqrt(M))*Q_fctn(np.sqrt(<NUM_LIT:3>/(M-<NUM_LIT:1>)*<NUM_LIT:10>**(EsN0_dB/<NUM_LIT:10>)))<EOL>BEP = SEP/np.log2(M)<EOL><DEDENT>return BEP<EOL>

Approximate the bit error probability of QAM assuming Gray encoding

Mark Wickert November 2018


w = Gdk.get_default_root_window()<EOL>if bbox is not None:<EOL><INDENT>g = [bbox[<NUM_LIT:0>], bbox[<NUM_LIT:1>], bbox[<NUM_LIT:2>] - bbox[<NUM_LIT:0>], bbox[<NUM_LIT:3>] - bbox[<NUM_LIT:1>]]<EOL><DEDENT>else:<EOL><INDENT>g = w.get_geometry()<EOL><DEDENT>pb = Gdk.pixbuf_get_from_window(w, *g)<EOL>if pb.get_bits_per_sample() != <NUM_LIT:8>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>elif pb.get_n_channels() != <NUM_LIT:3>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>pixel_bytes = pb.read_pixel_bytes().get_data()  <EOL>width, height = g[<NUM_LIT:2>], g[<NUM_LIT:3>]<EOL>return Image.frombytes(<EOL>'<STR_LIT>', (width, height), pixel_bytes, '<STR_LIT>', '<STR_LIT>', pb.get_rowstride(), <NUM_LIT:1>)<EOL>

Grabs an image directly to a buffer.

        :param bbox: Optional tuple or list containing (x1, y1, x2, y2) coordinates
            of sub-region to capture.
        :return: PIL RGB image
        :raises: ValueError, if image data does not have 3 channels (RGB), each with 8
            bits.
        :rtype: Image


def grab_to_file(self, filename, bbox=None):

w = self.gtk.gdk.get_default_root_window()<EOL>Capture the whole screen.<EOL>if bbox is None:<EOL><INDENT>sz = w.get_size()<EOL>pb = self.gtk.gdk.Pixbuf(<EOL>self.gtk.gdk.COLORSPACE_RGB, False, <NUM_LIT:8>, sz[<NUM_LIT:0>], sz[<NUM_LIT:1>])  <EOL>pb = pb.get_from_drawable(<EOL>w, w.get_colormap(), <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, sz[<NUM_LIT:0>], sz[<NUM_LIT:1>])<EOL><DEDENT>Only capture what we need. The smaller the capture, the faster.<EOL>else:<EOL><INDENT>sz = [bbox[<NUM_LIT:2>] - bbox[<NUM_LIT:0>], bbox[<NUM_LIT:3>] - bbox[<NUM_LIT:1>]]<EOL>pb = self.gtk.gdk.Pixbuf(<EOL>self.gtk.gdk.COLORSPACE_RGB, False, <NUM_LIT:8>, sz[<NUM_LIT:0>], sz[<NUM_LIT:1>])<EOL>pb = pb.get_from_drawable(<EOL>w, w.get_colormap(), bbox[<NUM_LIT:0>], bbox[<NUM_LIT:1>], <NUM_LIT:0>, <NUM_LIT:0>, sz[<NUM_LIT:0>], sz[<NUM_LIT:1>])<EOL><DEDENT>assert pb<EOL>ftype = '<STR_LIT>'<EOL>if filename.endswith('<STR_LIT>'):<EOL><INDENT>ftype = '<STR_LIT>'<EOL><DEDENT>pb.save(filename, ftype)<EOL>

http://www.pygtk.org/docs/pygtk/class-gdkpixbuf.html.

        only "jpeg" or "png"


return not is_inside_idle()<EOL>

IDLE has problem with multiprocessing.

    Therefore the default is False for childprocess if the program was
    started inside IDLE.