Communications

Communications toolbox.

arlpy.comms.awgn(x, snr, measured=False, complex=None)

Add Gaussian noise to signal.

Parameters:
  • x – real passband or complex baseband signal
  • snr – SNR in dB
  • measured – True to measure signal strength, False to assume unit strength signal
  • complex – True for complex noise, False for real noise, None to automatically decide
>>> import arlpy
>>> d1 = arlpy.comms.random_data(100, 4)
>>> qpsk = arlpy.comms.psk(4)
>>> x = arlpy.comms.modulate(d1, qpsk)
>>> y = arlpy.comms.awgn(x, 6)
>>> d2 = arlpy.comms.demodulate(y, qpsk)
>>> arlpy.comms.ser(d1, d2)
0.02
arlpy.comms.ber(x, y, m=2)

Measure bit error rate between symbols in x and y.

Parameters:
  • x – symbol array #1
  • y – symbol array #2
  • m – symbol alphabet size (maximum 64)
Returns:

bit error rate

>>> import arlpy
>>> arlpy.comms.ber([0,1,2,3], [0,1,2,2], m=4)
0.125
arlpy.comms.bi2sym(x, m)

Convert bits to symbols.

Parameters:
  • x – bit array
  • m – symbol alphabet size (must be a power of 2)
Returns:

symbol array

>>> import arlpy
>>> arlpy.comms.bi2sym([0, 0, 1, 0, 1, 0, 1, 1, 1], 8)
array([1, 2, 7])
arlpy.comms.demodulate(x, const, metric=None, decision=<function <lambda>>)

Demodulate complex baseband signal based on the specified constellation.

Parameters:
  • x – complex baseband signal to demodulate
  • const – constellation to use
  • metric – distance metric to use as a measure of closeness of signals
  • decision – rule for decision making, None to return soft decisions
Returns:

demodulated data symbols (or metric in soft decision mode)

The metric is a function that takes in two signal segments and measures a “distance” between them. The smaller the distance (allowed to be negative), the closer the signals. Usually one signal is from the constellation while the other is a segment of the input signal to demodulate. When unspecified, the metric for a complex valued constellation (such as PSK/QAM) is the Euclidean distance, for a real valued constellation (such as OOK) is the incoherent difference in signal level, and for a vector valued constellation is the dot product.

The decision rule is a function that takes in the metric of all possible constellation points and decides on the demodulated data. By default, this is the argmin function. If the decision rule is set to None, no hard decision is made, and the metric is returned as a “soft decision”.

>>> import arlpy
>>> bpsk = arlpy.comms.psk()
>>> d1 = arlpy.comms.random_data(100)
>>> x = arlpy.comms.modulate(d1, bpsk)
>>> d2 = arlpy.comms.demodulate(x, bpsk)
>>> arlpy.comms.ber(d1, d2)
0.0
arlpy.comms.diff_decode(x)

Decode phase differential baseband signal.

Parameters:x – complex baseband differential data to decode
Returns:decoded complex baseband data of length len(x)-1
>>> import arlpy
>>> d1 = arlpy.comms.random_data(100, 4)
>>> qpsk = arlpy.comms.psk(4)
>>> x = arlpy.comms.modulate(d1, qpsk)
>>> y = arlpy.comms.diff_encode(x)
>>> z = arlpy.comms.diff_decode(y)
>>> d2 = arlpy.comms.demodulate(z, qpsk)
>>> arlpy.comms.ser(d1, d2)
0.0
arlpy.comms.diff_encode(x)

Encode phase differential baseband signal.

Parameters:x – complex baseband data to encode differentially
Returns:differentially encoded complex baseband data of length len(x)+1
>>> import arlpy
>>> x = arlpy.comms.modulate(arlpy.comms.random_data(100, 4), arlpy.comms.psk(4))   # QPSK
>>> len(x)
100
>>> y = arlpy.comms.diff_encode(x)  # DQPSK
>>> len(y)
101
>>> x[0]
(0.707+0.707j)
>>> y[1]/y[0]
(0.707+0.707j)
arlpy.comms.downconvert(x, sps, fc, fs=2.0, g=None)

Downconvert a passband signal with a matched pulse shaping filter.

This function supports downconversion by an integer factor. For a more general baseband conversion (but without matched filtering), see arlpy.signal.pb2bb().

If the carrier frequency is 0, the input is assumed to be complex baseband, and only undergoes matched filtering and downsampling. If the pulse shape is None, a rectangular pulse shape is assumed.

The downconversion process introduces a group delay depending on the matched filter. It is usually (len(g)-1)/2 passband samples.

Parameters:
  • x – real passband data (or complex baseband data at passband sampling rate, if fc=0)
  • sps – number of passband samples per baseband symbol
  • fc – carrier frequency in Hz
  • fs – passband sampling rate
  • g – pulse shaping filter (for matched filtering)
>>> import arlpy
>>> d1 = arlpy.comms.random_data(100, 4)
>>> qpsk = arlpy.comms.psk(4)
>>> bb1 = arlpy.comms.modulate(d1, qpsk)
>>> rrc = arlpy.comms.rrcosfir(0.25, 6)
>>> pb = arlpy.comms.upconvert(bb1, 6, 27000, 108000, rrc)
>>> bb2 = arlpy.comms.downconvert(pb, 6, 27000, 108000, rrc)
>>> delay = (len(rrc)-1)/2   # compute passband group delay of rrc FIR filter
>>> delay = 2*delay/6        # compute baseband group delay after filtering twice
>>> d2 = arlpy.comms.demodulate(bb2[delay:-delay], qpsk)
>>> arlpy.comms.ser(d1, d2)
0.0
arlpy.comms.fsk(m=2, n=None)

Generate an m-FSK constellation.

The concept of signal constellation is generalized to allow vectors to enable representation of FSK as signal points. The signal constellation then becomes a set of vectors, each vector representing the baseband signal to be used when the corresponding symbol is to be transmitted.

The constellation is scaled for unit average energy per sample, assuming the symbols are randomly distributed.

If n is unspecified, 2m baseband samples are generated per symbol. This ensures an integral number of cycles per symbol, and hence continuous phase.

Parameters:
  • m – symbol alphabet size
  • n – number of baseband samples per symbol
>>> import arlpy
>>> x = arlpy.comms.modulate(arlpy.comms.random_data(100, m=2), arlpy.comms.fsk())
arlpy.comms.gray_code(m)

Generate a Gray code map of size m.

Parameters:m – symbol alphabet size
Returns:a mapping from integers (indices) to Gray coded integers
>>> import arlpy
>>> x = arlpy.comms.gray_code(8)
>>> x
array([0, 1, 3, 2, 6, 7, 5, 4])
>>> x[3]   # 3 => 2
2
arlpy.comms.invert_map(x)

Generate an inverse map.

Parameters:x – map, such as that generated by gray_code()
Returns:an inverse map y, such that y[x[j]] = j
>>> import arlpy
>>> x = arlpy.comms.gray_code(8)
>>> y = arlpy.comms.invert_map(x)
>>> x[2]
3
>>> y[3]
2
arlpy.comms.modulate(data, const)

Modulate data into signal points for the specified constellation.

The data must use only symbol alphabet defined for the specified constellation.

Parameters:
  • data – data symbols to modulate
  • const – constellation to use
Returns:

modulated complex baseband signal

>>> import arlpy
>>> x = arlpy.comms.modulate(arlpy.comms.random_data(100), arlpy.comms.psk())
arlpy.comms.ook()

Generate an on-off keying constellation.

The constellation represents the baseband values for bits 0 and 1 respectively. The constellation is scaled for unit average energy per bit, assuming the bits are randomly distributed.

>>> import arlpy
>>> arlpy.comms.ook()
array([0, 1.414])
arlpy.comms.pam(m=2, gray=True, centered=True)

Generate a PAM constellation with m signal points.

The constellation represents the baseband values for symbols 0 through m-1 respectively. The constellation is scaled for unit average energy per sample, assuming the symbols are randomly distributed.

Parameters:
  • m – symbol alphabet size
  • gray – True to use Gray coding, False otherwise
  • centered – True to center constellation around 0, False otherwise
>>> import arlpy
>>> arlpy.comms.pam()
array([-1, 1])
>>> arlpy.comms.pam(m=4, gray=False, centered=False)
array([0, 0.535, 1.069, 1.604])
arlpy.comms.psk(m=2, phase0=None, gray=True)

Generate a PSK constellation with m signal points.

The constellation represents the baseband values for symbols 0 through m-1 respectively. The constellation is scaled for unit average energy per sample, assuming the symbols are randomly distributed.

Parameters:
  • m – symbol alphabet size
  • phase0 – phase of the 0 symbol (None => pi/4 for QPSK, 0 otherwise)
  • gray – True to use Gray coding, False otherwise
>>> import arlpy
>>> arlpy.comms.psk()
array([1+0j, -1+0j])
>>> arlpy.comms.psk(4)
array([0.707+0.707j, -0.707+0.707j, 0.707-0.707j, -0.707-0.707j])
>>> arlpy.comms.iqplot(arlpy.comms.psk(4))
arlpy.comms.qam(m=16, gray=True)

Generate a QAM constellation with m signal points.

The constellation represents the baseband values for symbols 0 through m-1 respectively. The constellation is scaled for unit average energy per sample, assuming the symbols are randomly distributed.

Parameters:
  • m – symbol alphabet size (must be a square of an integer)
  • gray – True to use Gray coding, False otherwise
>>> import arlpy
>>> arlpy.comms.iqplot(arlpy.comms.qam(16))
arlpy.comms.random_data(size, m=2)

Generate random symbols in the range [0, m-1].

Parameters:
  • size – number of data points (or shape) to produce
  • m – symbol alphabet size
>>> import arlpy
>>> arlpy.comms.random_data(10)
array([1, 0, 0, 1, 1, 0, 1, 0, 1, 0])
>>> arlpy.comms.random_data(10, 4)
array([0, 2, 2, 3, 2, 3, 2, 0, 1, 0])
>>> arlpy.comms.random_data((2,2))
array([[0, 1],
       [0, 0]])
arlpy.comms.rcosfir(beta, sps, span=None)

Generates a raised cosine FIR filter.

Parameters:
  • beta – shape of the raised cosine filter (0-1)
  • sps – number of samples per symbol
  • span – length of the filter in symbols (None => automatic selection)
>>> import arlpy
>>> rc = arlpy.comms.rcosfir(0.25, 6)
>>> bb = arlpy.comms.modulate(arlpy.comms.random_data(100), arlpy.comms.psk())
>>> pb = arlpy.comms.upconvert(bb, 6, 27000, 18000, rc)
arlpy.comms.rrcosfir(beta, sps, span=None)

Generates a root raised cosine FIR filter.

Parameters:
  • beta – shape of the root raised cosine filter (0-1)
  • sps – number of samples per symbol
  • span – length of the filter in symbols (None => automatic selection)
>>> import arlpy
>>> rrc = arlpy.comms.rrcosfir(0.25, 6)
>>> bb = arlpy.comms.modulate(arlpy.comms.random_data(100), arlpy.comms.psk())
>>> pb = arlpy.comms.upconvert(bb, 6, 27000, 18000, rrc)
arlpy.comms.ser(x, y)

Measure symbol error rate between symbols in x and y.

Parameters:
  • x – symbol array #1
  • y – symbol array #2
Returns:

symbol error rate

>>> import arlpy
>>> arlpy.comms.ser([0,1,2,3], [0,1,2,2])
0.25
arlpy.comms.sym2bi(x, m)

Convert symbols to bits.

Parameters:
  • x – symbol array
  • m – symbol alphabet size (must be a power of 2)
Returns:

bit array

>>> import arlpy
>>> arlpy.comms.sym2bi([1, 2, 7], 8)
array([0, 0, 1, 0, 1, 0, 1, 1, 1])
arlpy.comms.upconvert(x, sps, fc, fs=2.0, g=None)

Upconvert a complex baseband signal with pulse shaping.

This function supports upconversion by an integer factor. For a more general passband conversion (but without pulse shaping), see arlpy.signal.bb2pb().

If the carrier frequency is 0, the upsampled (at passband sampling rate) and pulse shaped complex baseband data is returned. If the pulse shape is None, a rectangular pulse shape is assumed.

The upconversion process introduces a group delay depending on the pulse shaping filter. It is usually (len(g)-1)/2 passband samples. When g is None, the group delay is (sps-1)/2 passband samples.

Parameters:
  • x – complex baseband data
  • sps – number of passband samples per baseband symbol
  • fc – carrier frequency in Hz
  • fs – passband sampling rate
  • g – pulse shaping filter
>>> import arlpy
>>> rrc = arlpy.comms.rrcosfir(0.25, 6)
>>> bb = arlpy.comms.modulate(arlpy.comms.random_data(100), arlpy.comms.psk())
>>> pb = arlpy.comms.upconvert(bb, 6, 27000, 108000, rrc)