engineeringkit.communications#
Decibel conversions; wavelength, Friis free-space propagation and decibel
link budgets; antenna gain, effective aperture, directivity and the
half-wave dipole (scipy.special.sici); Johnson-Nyquist thermal noise,
the cascaded noise factor and the output SNR of AM and FM; Nyquist
signalling and raised-cosine pulse shaping, Hartley’s law, entropy, Huffman
coding and Shannon capacity; AM, DSB-SC and FM modulation with Carson’s
bandwidth rule; the matched filter, BPSK/QPSK, Gray-labelled M-PSK and
square M-QAM and their AWGN error rates (scipy.special.erfc, Craig’s
integral by scipy.integrate.quad); PCM, mu-law companding and delta
modulation; the Hamming(7,4) code, cyclic redundancy checks, and
convolutional codes with Viterbi decoding; Rayleigh fading and diversity
combining; and OFDM via numpy.fft.
engineeringkit.communications: modulation, noise, capacity, detection, coding, fading and link budgets.
Hertz wavelengths and Friis free-space propagation with decibel link budgets; antenna gain, effective aperture and the half-wave dipole; Johnson-Nyquist thermal noise, Friis’s cascaded noise factor and the output SNR of AM and FM; Nyquist signalling, Hartley’s line rate, Shannon entropy and capacity, and Huffman coding; AM, suppressed-carrier and FM modulation with Carson’s bandwidth rule; raised-cosine pulse shaping; the matched filter, BPSK/QPSK, M-PSK and M-QAM and their AWGN error rates; PCM, mu-law companding and delta modulation; Hamming(7,4), CRC and convolutional codes with Viterbi decoding; Rayleigh fading and diversity combining; and DFT-based OFDM. Models are hand-written closed forms; NumPy’s FFT and SciPy’s erfc, sine/cosine integrals, quadrature and trapezoidal integration are used where they are the standard tool.
- class engineeringkit.communications.HammingDecodeResult(data, syndromes, corrected)[source]#
Bases:
objectOutput of
engineeringkit.communications.hamming74_decode().- Parameters:
data (ndarray)
syndromes (ndarray)
corrected (ndarray)
- corrected: ndarray#
Codewords after correction.
- Type:
ndarray of int
- data: ndarray#
Decoded data bits, four per codeword.
- Type:
ndarray of int
- summary()[source]#
Number of words decoded and number in which a bit was corrected.
- Return type:
dict
- syndromes: ndarray#
Per-word syndrome, the 1-based position of the corrected bit (0 if none).
- Type:
ndarray of int
- class engineeringkit.communications.HuffmanCode(probabilities, codewords)[source]#
Bases:
objectOutput of
engineeringkit.communications.huffman_code(): a prefix-free code table.- Parameters:
probabilities (ndarray)
codewords (tuple)
- property average_length: float#
Expected codeword length \(\bar L = \sum p_i \ell_i\) [bit per symbol].
- codewords: tuple#
Binary codeword of each symbol, such as
"010".- Type:
tuple of str
- decode(bits)[source]#
Split a bit stream back into symbol indices; the prefix property makes this unambiguous.
- Parameters:
bits (array_like) – Binary stream made of whole codewords.
- Returns:
The decoded symbol indices.
- Return type:
ndarray
- encode(symbols)[source]#
Concatenate the codewords of a sequence of symbol indices.
- Parameters:
symbols (array_like of int) – Indices into
probabilities.- Returns:
The encoded bit stream.
- Return type:
ndarray
- property entropy: float#
Source entropy \(H = -\sum p_i \log_2 p_i\) [bit per symbol], the lower bound on
average_length.
- property lengths: ndarray#
Codeword length of each symbol [bit].
- plot(ax=None)[source]#
Bar chart of codeword lengths next to the ideal lengths \(-\log_2 p_i\); return
(fig, ax).- Parameters:
ax (matplotlib.axes.Axes, optional) – Axes to draw on; a new figure is created if omitted.
- Returns:
(fig, ax).- Return type:
tuple
- probabilities: ndarray#
Symbol probabilities, in the order given.
- Type:
ndarray of float
- class engineeringkit.communications.ViterbiDecodeResult(data, path_metric)[source]#
Bases:
objectOutput of
engineeringkit.communications.viterbi_decode().- Parameters:
data (ndarray)
path_metric (float)
- data: ndarray#
Decoded data bits, without the zero tail.
- Type:
ndarray of int
- path_metric: float#
Metric of the surviving path: the Hamming distance between the received bits and the re-encoded decision (hard decisions), or the squared Euclidean distance to its
±1symbols (soft decisions).- Type:
float
- engineeringkit.communications.am_output_snr(snr_baseband, mu=1.0, message_power=0.5)[source]#
Output SNR of conventional AM with an envelope detector above threshold, \(\dfrac{\mu^2 P_m}{1 + \mu^2 P_m}\gamma\).
- Parameters:
snr_baseband (float or array_like) – Reference SNR \(\gamma\) from
baseband_snr()[-], nonnegative.mu (float or array_like, optional) – Modulation index [-] in
[0, 1].message_power (float or array_like, optional) – Mean square of the normalised message, \(P_m\) [-] in
[0, 1]; 0.5 for a full-scale tone.
- Returns:
Output SNR [-] (linear): at best one third of \(\gamma\), for a tone at \(\mu = 1\).
- Return type:
float or ndarray
Notes
Valid when the carrier is well above the noise; below that the envelope detector’s output collapses. Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(float(am_output_snr(300.0)), 6) 100.0
- engineeringkit.communications.am_signal(message, t, fc, mu=1.0, carrier_amplitude=1.0)[source]#
Conventional (full-carrier) AM, \(s(t) = A_c[1 + \mu m(t)]\cos 2\pi f_c t\).
- Parameters:
message (array_like) – Normalised message \(m(t)\) with \(|m| \le 1\) [-].
t (array_like) – Sample times [s], same shape as
message.fc (float) – Carrier frequency [Hz], nonnegative.
mu (float, optional) – Modulation index [-], nonnegative;
mu > 1overmodulates and distorts an envelope detector’s output.carrier_amplitude (float, optional) – Carrier amplitude \(A_c\) [V].
- Returns:
Modulated waveform [V].
- Return type:
ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> am_signal([0.5], [0.0], 1.0, mu=0.5).tolist() [1.25]
- engineeringkit.communications.aperture_gain(area, f, efficiency=1.0, c=299792458.0)[source]#
Gain of an aperture antenna such as a dish, \(G = 4\pi\eta_a A/\lambda^2\).
- Parameters:
area (float or array_like) – Physical aperture area [m^2], positive.
f (float or array_like) – Frequency [Hz], positive.
efficiency (float or array_like, optional) – Aperture efficiency \(\eta_a\) [-] in
(0, 1]; 0.55 to 0.7 is typical of a parabolic dish.c (float, optional) – Propagation speed [m/s], positive.
- Returns:
Gain [-] (linear).
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(float(10 * np.log10(aperture_gain(np.pi * 1.5**2, 12e9, 0.6))), 2) 49.31
- engineeringkit.communications.baseband_snr(received_power, N0, message_bandwidth)[source]#
Reference SNR \(\gamma = P_R/(N_0 W)\) against which analog modulations are compared.
It is the output SNR of baseband transmission, and also of coherent DSB-SC and SSB, with the same received power.
- Parameters:
received_power (float or array_like) – Received signal power \(P_R\) [W], nonnegative.
N0 (float or array_like) – One-sided noise power spectral density [W/Hz], positive.
message_bandwidth (float or array_like) – Message bandwidth \(W\) [Hz], positive.
- Returns:
\(\gamma\) [-] (linear).
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(baseband_snr(1e-9, 1e-15, 1e4)) 100.0
- engineeringkit.communications.ber_bpsk_awgn(ebn0)[source]#
Bit-error rate of coherent BPSK in AWGN, \(Q(\sqrt{2E_b/N_0}) = \tfrac12\operatorname{erfc}\sqrt{E_b/N_0}\).
- Parameters:
ebn0 (float or array_like) – Energy per bit over noise spectral density, \(E_b/N_0\) [-] (linear, not dB), nonnegative.
- Returns:
Bit-error probability [-].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
V. A. Kotel’nikov, The Theory of Optimum Noise Immunity (1947; English translation, McGraw-Hill, 1959).
Examples
>>> f"{float(ber_bpsk_awgn(10 ** (9.6 / 10))):.1e}" '9.7e-06'
- engineeringkit.communications.ber_bpsk_mrc_rayleigh(mean_ebn0, branches)[source]#
Average BPSK bit-error rate with
L-branch maximal-ratio combining in independent Rayleigh fading.\[P_b = \left(\frac{1-\mu}{2}\right)^L \sum_{k=0}^{L-1}\binom{L-1+k}{k}\left(\frac{1+\mu}{2}\right)^k, \qquad \mu = \sqrt{\frac{\bar\gamma}{1+\bar\gamma}}\]- Parameters:
mean_ebn0 (float or array_like) – Mean \(E_b/N_0\) per branch [-] (linear), nonnegative.
branches (int) – Number of independently fading branches \(L\), at least 1.
- Returns:
Bit-error probability [-], falling as \(\bar\gamma^{-L}\).
- Return type:
float or ndarray
Notes
branches=1isber_bpsk_rayleigh(). Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.References
D. G. Brennan, “Linear diversity combining techniques,” Proc. IRE 47 (1959) 1075-1102.
Examples
>>> bool(np.isclose(ber_bpsk_mrc_rayleigh(10.0, 1), ber_bpsk_rayleigh(10.0))) True
- engineeringkit.communications.ber_bpsk_rayleigh(mean_ebn0)[source]#
Average bit-error rate of coherent BPSK in flat Rayleigh fading, \(\tfrac12\left(1 - \sqrt{\bar\gamma/(1+\bar\gamma)}\right)\).
- Parameters:
mean_ebn0 (float or array_like) – Mean \(E_b/N_0\) [-] (linear), nonnegative.
- Returns:
Bit-error probability [-], about \(1/(4\bar\gamma)\) at high SNR.
- Return type:
float or ndarray
Notes
The channel gain is assumed known at the receiver and constant over a bit. Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(float(ber_bpsk_rayleigh(100.0)), 6) 0.002481
- engineeringkit.communications.ber_qpsk_awgn(ebn0)[source]#
Bit-error rate of coherent Gray-labelled QPSK in AWGN; equal to BPSK per bit.
- Parameters:
ebn0 (float or array_like) – \(E_b/N_0\) [-] (linear), nonnegative.
- Returns:
Bit-error probability [-].
- Return type:
float or ndarray
Notes
The in-phase and quadrature rails are independent BPSK channels. See Communications: linear ratios, modulation phase and channel scope.
Examples
>>> bool(ber_qpsk_awgn(4.0) == ber_bpsk_awgn(4.0)) True
- engineeringkit.communications.binary_symmetric_capacity(p)[source]#
Capacity of the binary symmetric channel, \(C = 1 - H(p)\).
- Parameters:
p (float or array_like) – Crossover (bit-flip) probability [-] in
[0, 1].- Returns:
Capacity [bit per channel use].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(binary_symmetric_capacity(0.11), 3) 0.5
- engineeringkit.communications.bpsk(bits)[source]#
Binary phase-shift keying: map bits
0, 1to antipodal symbols-1, +1.- Parameters:
bits (array_like) – Binary data (any integer or float dtype holding only 0 and 1).
- Returns:
Real unit-energy symbols.
- Return type:
ndarray
Notes
Inputs are converted to float before arithmetic, so unsigned zeros do not wrap; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> bpsk([0, 1, 1]).tolist() [-1.0, 1.0, 1.0]
- engineeringkit.communications.carson_bandwidth(peak_deviation, message_bandwidth)[source]#
Carson’s rule for the 98%-power bandwidth of FM, \(B \approx 2(\Delta f + W)\).
- Parameters:
peak_deviation (float or array_like) – Peak frequency deviation \(\Delta f\) [Hz], nonnegative.
message_bandwidth (float or array_like) – Highest message frequency \(W\) [Hz], nonnegative.
- Returns:
Transmission bandwidth [Hz].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
J. R. Carson, “Notes on the theory of modulation,” Proc. IRE 10 (1922) 57-64.
Examples
>>> float(carson_bandwidth(75e3, 15e3)) 180000.0
- engineeringkit.communications.cascaded_noise_factor(gains, noise_factors)[source]#
Friis’s formula for the noise factor of amplifiers in cascade.
\[F = F_1 + \frac{F_2 - 1}{G_1} + \frac{F_3 - 1}{G_1 G_2} + \cdots\]- Parameters:
gains (array_like) – Linear available power gain of each stage [-], positive. The last stage’s gain does not affect the result.
noise_factors (array_like) – Linear noise factor of each stage [-], at least one.
- Returns:
Overall noise factor [-].
- Return type:
float
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
H. T. Friis, “Noise figures of radio receivers,” Proc. IRE 32 (1944) 419-422.
Examples
>>> round(cascaded_noise_factor([10.0, 10.0], [2.0, 3.0]), 6) 2.2
- engineeringkit.communications.convolutional_encode(bits, generators=(7, 5))[source]#
Rate-\(1/n\) feedforward convolutional encoder, terminated with \(K-1\) zero tail bits.
Each generator is an integer whose binary digits are the shift-register taps, most significant for the current input bit, so the octal pair
(0o7, 0o5)is the classic constraint-length-3 code with \(g_0 = 1 + D + D^2\) and \(g_1 = 1 + D^2\).- Parameters:
bits (array_like) – Data bits.
generators (sequence of int, optional) – At least two generator polynomials; the longest sets the constraint length \(K\).
- Returns:
Code bits, \(n\) per input bit (outputs of the generators in turn), for the data and the tail: \(n(L + K - 1)\) in all.
- Return type:
ndarray of int
Notes
See Communications: linear ratios, modulation phase and channel scope.
References
P. Elias, “Coding for noisy channels,” IRE Convention Record 3, part 4 (1955) 37-46.
Examples
>>> convolutional_encode([1, 0, 1, 1]).tolist() [1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1]
- engineeringkit.communications.convolutional_free_distance(generators=(7, 5))[source]#
Free distance of a convolutional code: the least weight of a path that leaves and rejoins the zero state.
- Parameters:
generators (sequence of int, optional) – Generator polynomials as in
convolutional_encode().- Returns:
Free distance \(d_{free}\). A maximum-likelihood decoder corrects any pattern of fewer than \(d_{free}/2\) errors spread across one such span.
- Return type:
int
Notes
Found by Dijkstra’s shortest-path search over the trellis with Hamming weights as edge lengths. See Communications: linear ratios, modulation phase and channel scope.
Examples
>>> convolutional_free_distance((0o7, 0o5)), convolutional_free_distance((0o171, 0o133)) (5, 10)
- engineeringkit.communications.crc_check(codeword, generator)[source]#
True when a received codeword leaves no remainder, i.e. no error was detected.
- Parameters:
codeword (array_like) – Received bits, message then check bits.
generator (int) – Generator polynomial with its leading term; see
crc_remainder().
- Returns:
Trueif \(g(x)\) divides the received polynomial. A degree- \(r\) generator with a nonzero constant term detects every burst of \(r\) or fewer errors.- Return type:
bool
Notes
See Communications: linear ratios, modulation phase and channel scope.
Examples
>>> crc_check([1, 0, 1, 1, 0, 1], 0b111), crc_check([1, 1, 1, 1, 0, 1], 0b111) (True, False)
- engineeringkit.communications.crc_encode(bits, generator)[source]#
Append the CRC check bits to a message, giving a codeword divisible by the generator.
- Parameters:
bits (array_like) – Message bits, most significant first.
generator (int) – Generator polynomial with its leading term; see
crc_remainder().
- Returns:
The message followed by its check bits.
- Return type:
ndarray of int
Notes
See Communications: linear ratios, modulation phase and channel scope.
Examples
>>> crc_encode([1, 0, 1, 1], 0b111).tolist() [1, 0, 1, 1, 0, 1]
- engineeringkit.communications.crc_remainder(bits, generator)[source]#
Cyclic-redundancy-check bits: the remainder of \(x^r m(x)\) divided by \(g(x)\) over GF(2).
- Parameters:
bits (array_like) – Message bits, most significant (highest power of \(x\)) first.
generator (int) – Generator polynomial \(g(x)\) of degree \(r \ge 1\) written as an integer with its leading term, such as
0b1011for \(x^3 + x + 1\) orCRC16_CCITT.
- Returns:
The \(r\) check bits, most significant first.
- Return type:
ndarray of int
Notes
Plain polynomial division: register preset to zero, no bit reflection and no final XOR (the “XMODEM” convention for CRC-16-CCITT). See Communications: linear ratios, modulation phase and channel scope.
References
W. W. Peterson and D. T. Brown, “Cyclic codes for error detection,” Proc. IRE 49 (1961) 228-235.
Examples
>>> crc_remainder([1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0], 0b1011).tolist() [1, 0, 0]
- engineeringkit.communications.db(x)[source]#
Power ratio in decibels, \(10\log_{10} x\).
- Parameters:
x (float or array_like) – Linear power ratio [-], positive.
- Returns:
Ratio [dB].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(db(100.0)) 20.0
- engineeringkit.communications.dbm(power_w)[source]#
Power in decibels relative to one milliwatt.
- Parameters:
power_w (float or array_like) – Power [W], positive.
- Returns:
Power [dBm].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(dbm(1.0)) 30.0
- engineeringkit.communications.delta_demodulate(bits, step, initial=0.0)[source]#
Staircase reconstruction of a delta-modulated signal, \(\hat x_n = \hat x_0 + \Delta\sum(2b_k - 1)\).
- Parameters:
bits (array_like) – Bits from
delta_modulate().step (float) – Step size \(\Delta\), positive.
initial (float, optional) – Starting value of the staircase.
- Returns:
Staircase samples [same unit as
step]; a low-pass filter smooths them into the recovered waveform.- Return type:
ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> delta_demodulate([1, 1, 1, 0, 0], 0.25).tolist() [0.25, 0.5, 0.75, 0.5, 0.25]
- engineeringkit.communications.delta_modulate(x, step, initial=0.0)[source]#
Linear delta modulation: one bit per sample saying whether to step the staircase up or down.
The encoder tracks its own staircase approximation \(\hat x\): bit \(b_n = 1\) if \(x_n \ge \hat x_{n-1}\), and then \(\hat x_n = \hat x_{n-1} \pm \Delta\).
- Parameters:
x (array_like) – Samples [V or any amplitude unit], taken well above the Nyquist rate.
step (float) – Step size \(\Delta\) [same unit], positive.
initial (float, optional) – Starting value of the staircase [same unit].
- Returns:
One bit per sample;
delta_demodulate()rebuilds the staircase.- Return type:
ndarray of int
Notes
A small step causes slope overload where the signal changes faster than
delta_slope_limit(); a large one causes granular noise where it is flat. Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.References
F. de Jager, “Delta modulation, a method of P.C.M. transmission using the 1-unit code,” Philips Res. Rep. 7 (1952) 442-466.
Examples
>>> delta_modulate([0.0, 0.3, 0.6, 0.6, 0.2], 0.25).tolist() [1, 1, 1, 0, 0]
- engineeringkit.communications.delta_slope_limit(step, fs)[source]#
Fastest slope a delta modulator can follow without overload, \(\Delta f_s\).
- Parameters:
step (float or array_like) – Step size \(\Delta\) [V or any amplitude unit], positive.
fs (float or array_like) – Sampling rate [Hz], positive.
- Returns:
Slope limit [amplitude unit per second]. A sine of amplitude \(A\) and frequency \(f\) avoids overload when \(2\pi f A \le \Delta f_s\).
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(delta_slope_limit(0.01, 64e3)) 640.0
- engineeringkit.communications.directivity(theta, power_pattern)[source]#
Directivity of an azimuthally symmetric pattern, \(D = 2U_{max}\big/\int_0^\pi U(\theta)\sin\theta\,d\theta\).
- Parameters:
theta (array_like) – Increasing polar angles [rad] covering
[0, pi].power_pattern (array_like) – Radiation intensity \(U(\theta)\) at those angles [any unit], nonnegative, not all zero.
- Returns:
Directivity [-] (linear), by the trapezoidal rule; 1.5 for a short (Hertzian) dipole, whose pattern is \(\sin^2\theta\).
- Return type:
float
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> theta = np.linspace(0, np.pi, 2001) >>> round(directivity(theta, np.sin(theta) ** 2), 5) 1.5
- engineeringkit.communications.dsb_sc(message, t, fc)[source]#
Double-sideband suppressed-carrier modulation, \(s(t) = m(t)\cos 2\pi f_c t\).
- Parameters:
message (array_like) – Message samples [V].
t (array_like) – Sample times [s].
fc (float) – Carrier frequency [Hz], nonnegative.
- Returns:
Modulated waveform [V]; demodulate by multiplying with a coherent carrier and low-pass filtering.
- Return type:
ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> dsb_sc([2.0], [0.0], 5.0).tolist() [2.0]
- engineeringkit.communications.effective_aperture(gain, f, c=299792458.0)[source]#
Effective aperture of an antenna, \(A_e = G\lambda^2/4\pi\).
- Parameters:
gain (float or array_like) – Antenna gain [-] (linear, not dBi), positive.
f (float or array_like) – Frequency [Hz], positive.
c (float, optional) – Propagation speed [m/s], positive.
- Returns:
Effective area [m^2]: the received power is the incident power density times this area.
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
H. T. Friis, “A note on a simple transmission formula,” Proc. IRE 34 (1946) 254-256.
Examples
>>> round(float(effective_aperture(1.0, 299792458.0)), 6) 0.079577
- engineeringkit.communications.entropy(probabilities)[source]#
Entropy of a discrete memoryless source, \(H = -\sum_i p_i\log_2 p_i\).
- Parameters:
probabilities (array_like) – Symbol probabilities [-], at least two, nonnegative and summing to one.
- Returns:
Entropy [bit per symbol], between zero and \(\log_2 M\).
- Return type:
float
Notes
entropy_binary()is the two-symbol case. Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.References
C. E. Shannon, “A mathematical theory of communication,” Bell Syst. Tech. J. 27 (1948) 379-423, 623-656.
Examples
>>> entropy([0.5, 0.25, 0.125, 0.125]) 1.75
- engineeringkit.communications.entropy_binary(p)[source]#
Binary entropy \(H(p) = -p\log_2 p - (1-p)\log_2(1-p)\).
- Parameters:
p (float or array_like) – Probability [-] in
[0, 1].- Returns:
Entropy [bit]; zero at
p = 0andp = 1, one atp = 0.5.- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
C. E. Shannon, “A mathematical theory of communication,” Bell Syst. Tech. J. 27 (1948) 379-423, 623-656.
Examples
>>> float(entropy_binary(0.5)) 1.0
- engineeringkit.communications.fm_output_snr(snr_baseband, beta, message_power=0.5)[source]#
Output SNR of FM with a limiter-discriminator above threshold, \(3\beta^2 P_m\gamma\).
- Parameters:
snr_baseband (float or array_like) – Reference SNR \(\gamma\) from
baseband_snr()[-], nonnegative.beta (float or array_like) – Modulation index \(\beta = \Delta f/W\) [-], nonnegative.
message_power (float or array_like, optional) – Mean square of the message normalised to \(|m| \le 1\), \(P_m\) [-] in
[0, 1]; 0.5 for a full-scale tone.
- Returns:
Output SNR [-] (linear). The improvement grows as the square of the deviation, so FM trades bandwidth (
carson_bandwidth()) for noise immunity. No pre-emphasis is assumed.- Return type:
float or ndarray
Notes
Valid only above the threshold of
fm_threshold_snr(). Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.References
M. G. Crosby, “Frequency modulation noise characteristics,” Proc. IRE 25 (1937) 472-514.
Examples
>>> float(fm_output_snr(100.0, 5.0)) 3750.0
- engineeringkit.communications.fm_signal(message, t, fc, kf)[source]#
Frequency modulation, \(\cos(2\pi f_c t + 2\pi k_f\int_0^t m\,d\tau)\).
The phase integral uses the trapezoidal rule, so nonuniform time grids are allowed; the modulation phase is zero at the first sample.
- Parameters:
message (array_like) – Message samples \(m(t)\).
t (array_like) – Strictly increasing sample times [s], same shape as
message.fc (float) – Carrier frequency [Hz], nonnegative.
kf (float) – Frequency sensitivity [Hz per message unit].
- Returns:
Unit-amplitude FM waveform.
- Return type:
ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
E. H. Armstrong, “A method of reducing disturbances in radio signaling by a system of frequency modulation,” Proc. IRE 24 (1936) 689-740.
Examples
>>> fm_signal([1.0, 1.0], [0.0, 0.25], 0.0, 1.0).round(12).tolist() [1.0, 0.0]
- engineeringkit.communications.fm_threshold_snr(beta, carrier_to_noise=10.0)[source]#
Reference SNR \(\gamma\) at the FM threshold, \(2(\beta + 1)\,\mathrm{CNR}_{th}\).
Below threshold, noise spikes (“clicks”) from phase wrap-arounds swamp the discriminator output and
fm_output_snr()no longer holds.- Parameters:
beta (float or array_like) – Modulation index [-], nonnegative.
carrier_to_noise (float, optional) – Carrier-to-noise ratio in the Carson bandwidth at threshold [-] (linear), positive; about 10 (10 dB) is customary.
- Returns:
Threshold value of \(\gamma = P_R/(N_0 W)\) [-] (linear).
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(fm_threshold_snr(5.0)) 120.0
- engineeringkit.communications.free_space_path_loss(distance, f, c=299792458.0)[source]#
Free-space path loss between isotropic antennas, \((4\pi d f / c)^2\).
- Parameters:
distance (float or array_like) – Separation [m], positive; far field is assumed.
f (float or array_like) – Frequency [Hz], positive.
c (float, optional) – Propagation speed [m/s].
- Returns:
Linear loss ratio [-], at least one in the far field.
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(float(free_space_path_loss_db(1000.0, 2.4e9)), 2) 100.05
- engineeringkit.communications.free_space_path_loss_db(distance, f, c=299792458.0)[source]#
Free-space path loss in decibels; see
free_space_path_loss().- Parameters:
distance (float or array_like) – Separation [m], positive.
f (float or array_like) – Frequency [Hz], positive.
c (float, optional) – Propagation speed [m/s].
- Returns:
Path loss [dB].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(float(free_space_path_loss_db(1.0, 299792458.0 / (4 * np.pi))), 6) 0.0
- engineeringkit.communications.friis_received_power(Pt, Gt, Gr, distance, f)[source]#
Friis transmission equation, \(P_r = P_t G_t G_r (\lambda/4\pi d)^2\).
- Parameters:
Pt (float or array_like) – Transmitted power [W], nonnegative.
Gt (float or array_like) – Transmit and receive antenna gains [-] (linear), positive.
Gr (float or array_like) – Transmit and receive antenna gains [-] (linear), positive.
distance (float or array_like) – Separation [m], positive.
f (float or array_like) – Frequency [Hz], positive.
- Returns:
Received power [W].
- Return type:
float or ndarray
Notes
Free space, far-field antennas and matched polarization and impedance are assumed. Inputs are raw SI numbers; see Communications: linear ratios, modulation phase and channel scope.
References
H. T. Friis, “A note on a simple transmission formula,” Proc. IRE 34 (1946) 254-256.
Examples
>>> round(float(friis_received_power(1.0, 1.0, 1.0, 1000.0, 2.4e9)) * 1e10, 4) 0.9881
- engineeringkit.communications.from_db(x_db)[source]#
Linear power ratio from decibels, \(10^{x/10}\).
- Parameters:
x_db (float or array_like) – Ratio [dB].
- Returns:
Linear power ratio [-].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(from_db(30.0)) 1000.0
- engineeringkit.communications.from_dbm(x)[source]#
Power in watts from dBm, \(10^{-3}\cdot 10^{x/10}\).
- Parameters:
x (float or array_like) – Power [dBm].
- Returns:
Power [W].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(from_dbm(30.0)) 1.0
- engineeringkit.communications.half_wave_dipole_directivity()[source]#
Directivity of a thin half-wave dipole, \(D = 4/\operatorname{Cin}(2\pi) \approx 1.641\) (2.15 dBi).
- Returns:
Directivity [-] (linear), from the closed-form integral of the pattern, \(\operatorname{Cin}(x) = \gamma + \ln x - \operatorname{Ci}(x)\).
- Return type:
float
Notes
See Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(half_wave_dipole_directivity(), 4) 1.6409
- engineeringkit.communications.half_wave_dipole_pattern(theta)[source]#
Normalised far-field pattern of a thin half-wave dipole, \(F(\theta) = \cos(\tfrac\pi2\cos\theta)/\sin\theta\).
- Parameters:
theta (float or array_like) – Angle from the dipole axis [rad].
- Returns:
Field-strength pattern [-], one broadside (\(\theta = \pi/2\)) and zero along the axis; square it for the power pattern.
- Return type:
float or ndarray
Notes
A sinusoidal current distribution is assumed. Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(half_wave_dipole_pattern(np.pi / 2)), float(half_wave_dipole_pattern(0.0)) (1.0, 0.0)
- engineeringkit.communications.half_wave_dipole_resistance(impedance=376.730313668)[source]#
Radiation resistance of a thin half-wave dipole, \(R = \dfrac{\eta}{4\pi}\operatorname{Cin}(2\pi) \approx 73.1\ \Omega\).
- Parameters:
impedance (float or array_like, optional) – Wave impedance of the medium \(\eta\) [ohm], positive.
- Returns:
Radiation resistance referred to the feed-point current [ohm].
- Return type:
float or ndarray
Notes
Carter’s induced-EMF method with a sinusoidal current. Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
P. S. Carter, “Circuit relations in radiating systems and applications to antenna problems,” Proc. IRE 20 (1932) 1004-1041.
Examples
>>> round(half_wave_dipole_resistance(), 2) 73.08
- engineeringkit.communications.hamming74_decode(codewords)[source]#
Decode Hamming(7,4) codewords, correcting up to one flipped bit per word.
- Parameters:
codewords (array_like) – Received binary words; length must be a multiple of seven.
- Returns:
Decoded data bits, the syndrome of each word (the 1-based position of the corrected bit, or 0), and the corrected codewords.
- Return type:
Notes
Two or more errors in one word are silently mis-corrected; the code’s minimum distance of three guarantees only single-error correction. See Communications: linear ratios, modulation phase and channel scope.
Examples
>>> received = hamming74_encode([1, 0, 1, 1]) >>> received[4] ^= 1 >>> result = hamming74_decode(received) >>> result.data.tolist(), result.syndromes.tolist() ([1, 0, 1, 1], [5])
- engineeringkit.communications.hamming74_encode(data)[source]#
Encode data bits with the Hamming(7,4) single-error-correcting code.
- Parameters:
data (array_like) – Binary data; its length must be a multiple of four.
- Returns:
Codewords
[p1, p2, d1, p3, d2, d3, d4]concatenated, 7/4 as long as the input. Any two codewords differ in at least three positions.- Return type:
ndarray of int
Notes
See Communications: linear ratios, modulation phase and channel scope.
References
R. W. Hamming, “Error detecting and error correcting codes,” Bell Syst. Tech. J. 29 (1950) 147-160.
Examples
>>> hamming74_encode([1, 0, 1, 1]).tolist() [0, 1, 1, 0, 0, 1, 1]
- engineeringkit.communications.hamming_distance(a, b)[source]#
Number of positions at which two equal-length binary words differ.
- Parameters:
a (array_like) – Binary words of the same shape.
b (array_like) – Binary words of the same shape.
- Returns:
Hamming distance.
- Return type:
int
Notes
See Communications: linear ratios, modulation phase and channel scope.
Examples
>>> hamming_distance([1, 0, 1, 1], [1, 1, 1, 0]) 2
- engineeringkit.communications.hartley_capacity(B, M)[source]#
Hartley’s line rate, \(2B\log_2 M\), for
Mdistinguishable levels.- Parameters:
B (float or array_like) – Bandwidth [Hz], nonnegative.
M (int) – Number of distinguishable signal levels, at least 2.
- Returns:
Bit rate [bit/s].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
R. V. L. Hartley, “Transmission of information,” Bell Syst. Tech. J. 7 (1928) 535-563.
Examples
>>> float(hartley_capacity(3000.0, 4)) 12000.0
- engineeringkit.communications.huffman_code(probabilities)[source]#
Huffman’s optimal binary prefix code for a discrete memoryless source.
The two least probable entries are merged into one, with a
0and a1prepended to their codewords, until a single entry remains. Ties are broken by order of creation, so the result is deterministic.- Parameters:
probabilities (array_like) – Symbol probabilities [-], at least two, nonnegative and summing to one.
- Returns:
Codeword table with
average_length,entropy,encodeanddecode. The average length satisfies \(H \le \bar L < H + 1\).- Return type:
Notes
See Communications: linear ratios, modulation phase and channel scope.
References
D. A. Huffman, “A method for the construction of minimum-redundancy codes,” Proc. IRE 40 (1952) 1098-1101.
Examples
>>> code = huffman_code([0.4, 0.2, 0.2, 0.1, 0.1]) >>> round(code.average_length, 6) 2.2
- engineeringkit.communications.link_budget_db(pt_dbm, gt_db, gr_db, path_loss_db, losses_db=0.0)[source]#
Received power from a decibel link budget, \(P_r = P_t + G_t + G_r - L_p - L\).
- Parameters:
pt_dbm (float or array_like) – Transmitted power [dBm].
gt_db (float or array_like) – Antenna gains [dBi].
gr_db (float or array_like) – Antenna gains [dBi].
path_loss_db (float or array_like) – Propagation loss [dB].
losses_db (float or array_like, optional) – Other losses (cables, pointing, atmosphere) [dB].
- Returns:
Received power [dBm].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(link_budget_db(30.0, 10.0, 10.0, 100.0, 2.0)) -52.0
- engineeringkit.communications.matched_filter(pulse)[source]#
Impulse response of the filter matched to a pulse, \(h[n] = s^*[N - 1 - n]\).
- Parameters:
pulse (array_like) – Samples of the known pulse \(s\) (real or complex).
- Returns:
The time-reversed, conjugated pulse. Convolving the received samples with it and sampling at the pulse’s end, sample \(N - 1\), gives the correlation \(\sum r[n] s^*[n]\).
- Return type:
ndarray
Notes
In white noise this filter maximises the output signal-to-noise ratio over all linear filters; see
matched_filter_snr()and Communications: linear ratios, modulation phase and channel scope.References
D. O. North, “An analysis of the factors which determine signal/noise discrimination in pulsed-carrier systems,” RCA Laboratories Report PTR-6C (25 June 1943); reprinted in Proc. IEEE 51 (1963) 1016-1027.
Examples
>>> matched_filter([1.0, 2.0, 3.0]).tolist() [3.0, 2.0, 1.0]
- engineeringkit.communications.matched_filter_snr(pulse_energy, N0)[source]#
Peak output signal-to-noise ratio of a matched filter in white noise, \(2E/N_0\).
- Parameters:
pulse_energy (float or array_like) – Received pulse energy \(E\) [J], nonnegative.
N0 (float or array_like) – One-sided noise power spectral density [W/Hz], positive (the two-sided density is \(N_0/2\)).
- Returns:
Output SNR [-] (linear). It depends only on the pulse energy, not on its shape.
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(matched_filter_snr(2e-9, 1e-9)) 4.0
- engineeringkit.communications.maximal_ratio_combine(received, gains)[source]#
Maximal-ratio combining of diversity branches, \(\hat s = \sum_l h_l^* r_l / \sum_l |h_l|^2\).
Each branch is weighted by its conjugate channel gain, so strong branches count for more and the output SNR is the sum of the branch SNRs.
- Parameters:
received (array_like) – Received samples, shape
(L, N): one row per branch.gains (array_like) – Complex channel gains \(h_l\), shape
(L, N)or(L,).
- Returns:
Combined estimate of the
Ntransmitted symbols (unit gain on the signal).- Return type:
ndarray of complex
Notes
Every column needs at least one nonzero gain. See Communications: linear ratios, modulation phase and channel scope.
References
Kahn, “Ratio squarer,” Proc. IRE 42 (1954) 1704.
Examples
>>> maximal_ratio_combine([[2.0], [1j]], [2.0, 1j]).tolist() [(1+0j)]
- engineeringkit.communications.minimum_distance_detect(received, constellation)[source]#
Index of the constellation point nearest each received sample (maximum likelihood in AWGN).
- Parameters:
received (array_like) – Received complex (or real) samples.
constellation (array_like) – Candidate points, for example from
psk_constellation()orqam_constellation().
- Returns:
The detected label of each sample.
- Return type:
ndarray of int
Notes
See Communications: linear ratios, modulation phase and channel scope.
Examples
>>> from engineeringkit.communications import psk_constellation >>> minimum_distance_detect([0.9 + 0.2j, -0.1 - 1.2j], psk_constellation(4)).tolist() [0, 2]
- engineeringkit.communications.mu_law_compress(x, mu=255.0, full_scale=1.0)[source]#
Mu-law compressor \(y = V\operatorname{sgn}(x)\ln(1 + \mu|x|/V)/\ln(1 + \mu)\).
Compressing before a uniform quantizer gives small signals finer steps than loud ones, so the SQNR stays nearly constant over a wide range of speech levels. North American and Japanese telephony use \(\mu = 255\) with 8-bit PCM.
- Parameters:
x (float or array_like) – Samples with \(|x| \le V\) [V or any amplitude unit].
mu (float, optional) – Compression parameter \(\mu\) [-], positive.
full_scale (float, optional) – Peak amplitude \(V\) [same unit], positive.
- Returns:
Compressed samples in \([-V, V]\).
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
B. Smith, “Instantaneous companding of quantized signals,” Bell Syst. Tech. J. 36 (1957) 653-709.
Examples
>>> float(mu_law_compress(1.0)), round(float(mu_law_compress(0.01)), 4) (1.0, 0.2285)
- engineeringkit.communications.mu_law_expand(y, mu=255.0, full_scale=1.0)[source]#
Inverse of
mu_law_compress(), \(x = V\operatorname{sgn}(y)[(1 + \mu)^{|y|/V} - 1]/\mu\).- Parameters:
y (float or array_like) – Compressed samples with \(|y| \le V\).
mu (float, optional) – Compression parameter \(\mu\) [-], positive.
full_scale (float, optional) – Peak amplitude \(V\), positive.
- Returns:
Expanded samples [same unit as
full_scale].- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(float(mu_law_expand(mu_law_compress(0.3))), 12) 0.3
- engineeringkit.communications.noise_figure(F)[source]#
Noise figure \(NF = 10\log_{10} F\) from a linear noise factor.
- Parameters:
F (float or array_like) – Noise factor [-], at least one (one for a noiseless device).
- Returns:
Noise figure [dB].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(noise_figure(2.0)) > 3.0 True
- engineeringkit.communications.nyquist_symbol_rate(B)[source]#
Maximum intersymbol-interference-free pulse rate through an ideal low-pass channel, \(2B\).
- Parameters:
B (float or array_like) – Channel bandwidth [Hz], nonnegative.
- Returns:
Symbol rate [baud].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
H. Nyquist, “Certain topics in telegraph transmission theory,” Trans. AIEE 47 (1928) 617-644.
Examples
>>> float(nyquist_symbol_rate(3000.0)) 6000.0
- engineeringkit.communications.ofdm_demodulate(samples, n_subcarriers, cyclic_prefix=0)[source]#
Recover subcarrier symbols from OFDM samples (inverse of
ofdm_modulate()).- Parameters:
samples (array_like) – Received baseband samples; the length must be a multiple of
n_subcarriers + cyclic_prefix.n_subcarriers (int) – Number of subcarriers, at least 2.
cyclic_prefix (int, optional) – Cyclic-prefix length used by the transmitter.
- Returns:
One complex value per subcarrier per block. After a channel shorter than the cyclic prefix, each equals the transmitted symbol times the channel’s frequency response at that subcarrier.
- Return type:
ndarray of complex
Notes
See Communications: linear ratios, modulation phase and channel scope.
Examples
>>> rx = ofdm_demodulate(ofdm_modulate([1, -1, 1j, -1j], 4, 2), 4, 2) >>> np.allclose(rx, [1, -1, 1j, -1j]) True
- engineeringkit.communications.ofdm_modulate(symbols, n_subcarriers, cyclic_prefix=0)[source]#
Map complex symbols onto OFDM time-domain blocks with an inverse DFT.
- Parameters:
symbols (array_like) – Complex constellation symbols; the length must be a multiple of
n_subcarriers.n_subcarriers (int) – Number of subcarriers (IDFT size), at least 2.
cyclic_prefix (int, optional) – Number of samples copied from the end of each block to its front, nonnegative and less than
n_subcarriers.
- Returns:
Baseband samples,
n_subcarriers + cyclic_prefixper block, normalised so average power equals average symbol energy.- Return type:
ndarray of complex
Notes
See Communications: linear ratios, modulation phase and channel scope.
References
S. B. Weinstein and P. M. Ebert, “Data transmission by frequency-division multiplexing using the discrete Fourier transform,” IEEE Trans. Commun. Technol. 19 (1971) 628-634.
Examples
>>> x = ofdm_modulate([1, 1, 1, 1], 4, cyclic_prefix=1) >>> np.round(x.real, 12).tolist() [0.0, 2.0, 0.0, 0.0, 0.0]
- engineeringkit.communications.pcm_decode(bits, n_bits, full_scale=1.0)[source]#
Reconstruct PCM samples at the centres of their quantization intervals.
- Parameters:
bits (array_like) – Bit stream from
pcm_encode(), \(N\) bits per sample.n_bits (int) – Bits per sample \(N\), between 1 and 52.
full_scale (float, optional) – Peak amplitude \(V\), positive.
- Returns:
Reconstructed samples \(-V + (k + 1/2)q\) [same unit as
full_scale]; in range, each is within \(q/2\) of the input.- Return type:
ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> pcm_decode([0, 0, 0, 1, 0, 0], 3).tolist() [-0.875, 0.125]
- engineeringkit.communications.pcm_encode(x, n_bits, full_scale=1.0)[source]#
Pulse-code modulation: quantize samples uniformly and write each level number in binary.
The quantizer is mid-rise over \([-V, V]\) with step \(q = 2V/2^N\); samples outside the range saturate at the end levels.
- Parameters:
x (array_like) – Samples [V or any amplitude unit].
n_bits (int) – Bits per sample \(N\), between 1 and 52.
full_scale (float, optional) – Peak amplitude \(V\) [same unit], positive.
- Returns:
\(N\) bits per sample, most significant first, the level number counted upward from \(-V\).
- Return type:
ndarray of int
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
A. H. Reeves, French Patent 852,183 (filed 1938, published 1939); U.S. Patent 2,272,070 (1942).
Examples
>>> pcm_encode([-0.9, 0.1, 0.99], 3).tolist() [0, 0, 0, 1, 0, 0, 1, 1, 1]
- engineeringkit.communications.pcm_sqnr_db(n_bits)[source]#
Signal-to-quantization-noise ratio of a full-scale sine through an
N-bit PCM quantizer.With noise power \(q^2/12\) and signal power \(V^2/2\), the ratio is \(\tfrac32 4^N\), or \(6.02N + 1.76\) dB.
- Parameters:
n_bits (int) – Bits per sample \(N\), between 1 and 52.
- Returns:
SQNR [dB]. It is the large-\(N\) value: a simulated full-scale sine falls short by about \(2.3 \cdot 2^{-N/2}\) dB (0.14 dB at 8 bits), because the sine dwells near its peaks, where the error in the top interval is not uniform.
- Return type:
float
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
B. M. Oliver, J. R. Pierce and C. E. Shannon, “The philosophy of PCM,” Proc. IRE 36 (1948) 1324-1331.
Examples
>>> round(pcm_sqnr_db(8), 2) 49.93
- engineeringkit.communications.psk_constellation(M, phase_offset=0.0)[source]#
Gray-labelled M-ary phase-shift-keying constellation on the unit circle.
- Parameters:
M (int) – Number of phases, a power of two, at least 2.
phase_offset (float, optional) – Phase of the first point [rad].
- Returns:
Munit-energy points, indexed by label: entrykis the symbol sent for the bit patternk. Neighbouring phases carry labels that differ in one bit, so a symbol error to a neighbour costs one bit.- Return type:
ndarray of complex
Notes
The point at phase \(2\pi i/M + \phi_0\) carries the Gray label \(i \oplus \lfloor i/2 \rfloor\). See Communications: linear ratios, modulation phase and channel scope.
References
C. R. Cahn, “Performance of digital phase-modulation communication systems,” IRE Trans. Commun. Syst. 7 (1959) 3-6.
Examples
>>> np.round(np.angle(psk_constellation(4), deg=True)).tolist() [0.0, 90.0, -90.0, 180.0]
- engineeringkit.communications.q_function(x)[source]#
Gaussian tail probability \(Q(x) = \tfrac12\operatorname{erfc}(x/\sqrt2)\).
- Parameters:
x (float or array_like) – Threshold in standard deviations [-].
- Returns:
\(P(Z > x)\) for a standard normal \(Z\).
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(q_function(0.0)) 0.5
- engineeringkit.communications.qam_constellation(M)[source]#
Gray-labelled square M-ary quadrature-amplitude-modulation constellation with unit average energy.
The in-phase and quadrature rails each carry \(\sqrt M\) equally spaced levels \(\pm 1, \pm 3, \ldots\), scaled by \(\sqrt{3/2(M-1)}\) so that the average symbol energy is one.
- Parameters:
M (int) – Number of points, a power of four, at least 4 (4, 16, 64, …).
- Returns:
Mpoints indexed by label; the high half of the label’s bits is the Gray code of the in-phase level and the low half that of the quadrature level.- Return type:
ndarray of complex
Notes
See Communications: linear ratios, modulation phase and channel scope.
References
C. N. Campopiano and B. G. Glazer, “A coherent digital amplitude and phase modulation scheme,” IRE Trans. Commun. Syst. 10 (1962) 90-95.
Examples
>>> points = qam_constellation(16) >>> round(float(np.mean(np.abs(points) ** 2)), 12) 1.0
- engineeringkit.communications.qpsk(bit_pairs)[source]#
Quadrature phase-shift keying: two bits per unit-energy complex symbol.
The first bit of each pair sets the sign of the in-phase component and the second the quadrature component (Gray labelling), so each symbol is \((\pm 1 \pm j)/\sqrt2\).
- Parameters:
bit_pairs (array_like) – An even number of binary values.
- Returns:
Complex symbols, half as many as bits.
- Return type:
ndarray
Notes
See Communications: linear ratios, modulation phase and channel scope.
References
C. R. Cahn, “Combined digital phase and amplitude modulation communication systems,” IRE Trans. Commun. Syst. 8 (1960) 150-155.
Examples
>>> np.round(qpsk([1, 1]) * np.sqrt(2), 12).tolist() [(1+1j)]
- engineeringkit.communications.raised_cosine_bandwidth(symbol_rate, rolloff)[source]#
Occupied (absolute) bandwidth of raised-cosine signalling, \(B = (1 + \alpha)R_s/2\).
- Parameters:
symbol_rate (float or array_like) – Symbol rate \(R_s = 1/T\) [baud], nonnegative.
rolloff (float or array_like) – Roll-off factor \(\alpha\) [-] in
[0, 1].
- Returns:
Baseband bandwidth [Hz]; a passband (double-sideband) signal occupies twice this.
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(raised_cosine_bandwidth(2400.0, 0.25)) 1500.0
- engineeringkit.communications.raised_cosine_pulse(t, symbol_period, rolloff)[source]#
Raised-cosine pulse \(p(t) = \operatorname{sinc}(t/T)\,\dfrac{\cos(\pi\alpha t/T)}{1 - (2\alpha t/T)^2}\).
- Parameters:
t (float or array_like) – Time [s].
symbol_period (float or array_like) – Symbol period \(T\) [s], positive.
rolloff (float or array_like) – Roll-off (excess-bandwidth) factor \(\alpha\) [-] in
[0, 1]; zero gives Nyquist’s ideal sinc pulse.
- Returns:
Pulse value [-], one at \(t = 0\) and zero at every other multiple of \(T\).
- Return type:
float or ndarray
Notes
The removable singularity at \(|t| = T/2\alpha\) is replaced by its limit \((\pi/4)\operatorname{sinc}(1/2\alpha)\). Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
H. Nyquist, “Certain topics in telegraph transmission theory,” Trans. AIEE 47 (1928) 617-644.
Examples
>>> np.abs(raised_cosine_pulse([0.0, 1.0, 2.0], 1.0, 0.35)).round(12).tolist() [1.0, 0.0, 0.0]
- engineeringkit.communications.raised_cosine_spectrum(f, symbol_period, rolloff)[source]#
Fourier transform of
raised_cosine_pulse(): flat, then a half-cosine roll-off.\[\begin{split}P(f) = \begin{cases} T, & |f| \le \dfrac{1-\alpha}{2T} \\ \dfrac{T}{2}\left[1 + \cos\dfrac{\pi T}{\alpha}\left(|f| - \dfrac{1-\alpha}{2T}\right)\right], & \dfrac{1-\alpha}{2T} < |f| \le \dfrac{1+\alpha}{2T} \\ 0, & \text{otherwise.} \end{cases}\end{split}\]- Parameters:
f (float or array_like) – Frequency [Hz].
symbol_period (float or array_like) – Symbol period \(T\) [s], positive.
rolloff (float or array_like) – Roll-off factor \(\alpha\) [-] in
[0, 1].
- Returns:
Spectrum [s]. Copies shifted by every multiple of \(1/T\) add up to the constant \(T\), which is Nyquist’s criterion. With \(\alpha = 0\) the band edge takes the midpoint value \(T/2\).
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(raised_cosine_spectrum(0.25, 1.0, 0.5) + raised_cosine_spectrum(0.25 - 1.0, 1.0, 0.5)) 1.0
- engineeringkit.communications.rayleigh_pdf(r, mean_power=1.0)[source]#
Rayleigh density of a fading envelope, \(f(r) = \dfrac{2r}{\Omega}e^{-r^2/\Omega}\).
- Parameters:
r (float or array_like) – Envelope amplitude [V or any amplitude unit], nonnegative.
mean_power (float, optional) – Mean square envelope \(\Omega = E[r^2]\) [amplitude unit squared], positive.
- Returns:
Probability density [1/amplitude unit]. The power \(r^2\) is exponentially distributed with mean \(\Omega\).
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
Lord Rayleigh, “On the resultant of a large number of vibrations of the same pitch and of arbitrary phase,” Phil. Mag. 10 (1880) 73-78.
Examples
>>> round(float(rayleigh_pdf(np.sqrt(0.5))), 6) 0.857764
- engineeringkit.communications.root_raised_cosine_pulse(t, symbol_period, rolloff)[source]#
Unit-energy root-raised-cosine pulse, whose spectrum is \(\sqrt{P(f)}\).
\[h(t) = \frac{1}{\sqrt T}\, \frac{\sin[\pi(1-\alpha)t/T] + 4\alpha (t/T)\cos[\pi(1+\alpha)t/T]} {\pi (t/T)\,[1 - (4\alpha t/T)^2]}\]- Parameters:
t (float or array_like) – Time [s].
symbol_period (float or array_like) – Symbol period \(T\) [s], positive.
rolloff (float or array_like) – Roll-off factor \(\alpha\) [-] in
[0, 1].
- Returns:
Pulse value [s^-1/2]. The pulse convolved with itself is the raised-cosine pulse, so a root-raised-cosine transmit filter and the same filter as the receiver’s matched filter give zero intersymbol interference together.
- Return type:
float or ndarray
Notes
The singular points \(t = 0\) and \(|t| = T/4\alpha\) take their limits. Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(float(root_raised_cosine_pulse(0.0, 1.0, 0.0)), 12) 1.0
- engineeringkit.communications.selection_outage_probability(threshold_snr, mean_snr, branches=1)[source]#
Probability that the best of
LRayleigh branches is below a threshold, \((1 - e^{-\gamma_t/\bar\gamma})^L\).- Parameters:
threshold_snr (float or array_like) – SNR the receiver needs, \(\gamma_t\) [-] (linear), nonnegative.
mean_snr (float or array_like) – Mean SNR of each branch \(\bar\gamma\) [-] (linear), positive.
branches (int, optional) – Number of independently fading branches \(L\) for selection combining, at least 1.
- Returns:
Outage probability [-].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(float(selection_outage_probability(1.0, 10.0, 2)), 6) 0.009056
- engineeringkit.communications.ser_mpsk_awgn(esn0, M)[source]#
Exact symbol-error rate of coherent M-PSK in AWGN, by Craig’s integral.
\[P_s = \frac{1}{\pi}\int_0^{(M-1)\pi/M} \exp\left(-\frac{E_s}{N_0}\frac{\sin^2(\pi/M)}{\sin^2\theta}\right)d\theta\]- Parameters:
esn0 (float or array_like) – Energy per symbol over noise spectral density, \(E_s/N_0\) [-] (linear), nonnegative; \(E_s = E_b\log_2 M\).
M (int) – Number of phases, at least 2.
- Returns:
Symbol-error probability [-]. At high SNR it approaches \(2Q(\sqrt{2E_s/N_0}\sin(\pi/M))\); with Gray labelling the bit-error rate is close to \(P_s/\log_2 M\).
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
J. W. Craig, “A new, simple and exact result for calculating the probability of error for two-dimensional signal constellations,” IEEE MILCOM (1991) 571-575.
Examples
>>> bool(np.isclose(ser_mpsk_awgn(4.0, 2), ber_bpsk_awgn(4.0))) True
- engineeringkit.communications.ser_mqam_awgn(esn0, M)[source]#
Exact symbol-error rate of coherent square M-QAM in AWGN.
\[P_s = 1 - \left[1 - 2\left(1 - \frac{1}{\sqrt M}\right)Q\left(\sqrt{\frac{3E_s}{(M-1)N_0}}\right)\right]^2\]- Parameters:
esn0 (float or array_like) – \(E_s/N_0\) [-] (linear), nonnegative.
M (int) – Number of points, a power of four, at least 4.
- Returns:
Symbol-error probability [-]; each rail is an independent \(\sqrt M\)-level amplitude decision.
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> bool(np.isclose(ser_mqam_awgn(10.0, 4), ser_mpsk_awgn(10.0, 4))) True
- engineeringkit.communications.shannon_capacity(B, snr_linear)[source]#
Shannon-Hartley capacity of a band-limited AWGN channel, \(C = B\log_2(1 + S/N)\).
- Parameters:
B (float or array_like) – Bandwidth [Hz], nonnegative.
snr_linear (float or array_like) – Signal-to-noise power ratio [-] (linear), nonnegative.
- Returns:
Capacity [bit/s]: the highest rate at which arbitrarily reliable communication is possible with ideal coding.
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
C. E. Shannon, “Communication in the presence of noise,” Proc. IRE 37 (1949) 10-21.
Examples
>>> round(float(shannon_capacity(3000.0, 1000.0))) 29902
- engineeringkit.communications.spectral_efficiency(bit_rate, B)[source]#
Spectral efficiency \(R_b/B\).
- Parameters:
bit_rate (float or array_like) – Bit rate [bit/s], nonnegative.
B (float or array_like) – Occupied bandwidth [Hz], positive.
- Returns:
Efficiency [bit/s/Hz].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(spectral_efficiency(1e6, 2.5e5)) 4.0
- engineeringkit.communications.symbol_rate(bit_rate, M)[source]#
Symbol (baud) rate for an
M-ary alphabet, \(R_b / \log_2 M\).- Parameters:
bit_rate (float or array_like) – Bit rate [bit/s], nonnegative.
M (int) – Alphabet size, at least 2.
- Returns:
Symbol rate [baud].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> float(symbol_rate(9600.0, 16)) 2400.0
- engineeringkit.communications.thermal_noise_power(B, T=290.0, k=1.380649e-23)[source]#
Available thermal (Johnson-Nyquist) noise power \(N = kTB\).
- Parameters:
B (float or array_like) – Noise bandwidth [Hz], nonnegative.
T (float or array_like, optional) – Absolute noise temperature [K], nonnegative; 290 K is the standard reference.
k (float, optional) – Boltzmann constant [J/K].
- Returns:
Noise power [W] (-174 dBm in 1 Hz at 290 K).
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
References
J. B. Johnson, “Thermal agitation of electricity in conductors,” Phys. Rev. 32 (1928) 97-109; H. Nyquist, Phys. Rev. 32 (1928) 110-113.
Examples
>>> round(float(10 * np.log10(thermal_noise_power(1.0) / 1e-3)), 2) -173.98
- engineeringkit.communications.viterbi_decode(received, generators=(7, 5), soft=False)[source]#
Maximum-likelihood decoding of a terminated convolutional code by the Viterbi algorithm.
At each step every trellis state keeps only the best of the paths entering it (its survivor), so the work grows linearly with the message length instead of exponentially.
- Parameters:
received (array_like) – Received code sequence from
convolutional_encode(): hard bits (soft=False) or real soft values with bit 1 sent as+1and bit 0 as-1, as inbpsk()(soft=True). The length must be a multiple of the number of generators and cover the tail.generators (sequence of int, optional) – Generator polynomials used by the encoder.
soft (bool, optional) – Use the squared Euclidean distance (soft decisions, optimal in white Gaussian noise) instead of the Hamming distance.
- Returns:
Decoded data bits and the surviving path’s metric.
- Return type:
Notes
The encoder must have been terminated in the zero state, as
convolutional_encode()does. See Communications: linear ratios, modulation phase and channel scope.References
A. J. Viterbi, “Error bounds for convolutional codes and an asymptotically optimum decoding algorithm,” IEEE Trans. Inf. Theory 13 (1967) 260-269.
Examples
>>> code = convolutional_encode([1, 0, 1, 1]) >>> code[[2, 7]] ^= 1 >>> viterbi_decode(code).data.tolist() [1, 0, 1, 1]
- engineeringkit.communications.wavelength(f, c=299792458.0)[source]#
Free-space wavelength \(\lambda = c/f\).
- Parameters:
f (float or array_like) – Frequency [Hz], positive.
c (float, optional) – Propagation speed [m/s], positive.
- Returns:
Wavelength [m].
- Return type:
float or ndarray
Notes
Inputs are raw SI numbers with no dimensional metadata; see Communications: linear ratios, modulation phase and channel scope.
Examples
>>> round(float(wavelength(100e6)), 4) 2.9979
Visualizers#
Matplotlib visualizers for engineeringkit.communications.
- engineeringkit.communications.visualizers.plot_antenna_pattern(theta, pattern, ax=None, label=None, db_range=None)[source]#
Polar plot of an antenna’s radiation pattern against the angle from its axis.
- Parameters:
theta (array_like) – Angles [rad].
pattern (array_like) – Matching nonnegative pattern values (field or power), normalised or not.
ax (matplotlib.axes.Axes, optional) – Polar axes to draw on; a new figure is created if omitted.
label (str, optional) – Legend label.
db_range (float, optional) – If given, plot the power pattern in decibels relative to its peak, clipped this many dB below it.
- Return type:
matplotlib.axes.Axes
- engineeringkit.communications.visualizers.plot_ber_curve(ebn0_db, ber, ax=None, label=None)[source]#
Semilog plot of bit-error rate against \(E_b/N_0\) in decibels.
- Parameters:
ebn0_db (array_like) – \(E_b/N_0\) values [dB].
ber (array_like) – Matching bit-error rates [-]; zeros are omitted from the log axis.
ax (matplotlib.axes.Axes, optional) – Axes to draw on; a new figure is created if omitted.
label (str, optional) – Legend label.
- Return type:
matplotlib.axes.Axes
- engineeringkit.communications.visualizers.plot_constellation(symbols, ax=None, label=None)[source]#
Scatter complex symbols in the in-phase/quadrature plane.
- Parameters:
symbols (array_like) – Complex (or real) symbols.
ax (matplotlib.axes.Axes, optional) – Axes to draw on; a new figure is created if omitted.
label (str, optional) – Legend label.
- Return type:
matplotlib.axes.Axes
- engineeringkit.communications.visualizers.plot_eye_diagram(signal, samples_per_symbol, ax=None, spans=2, max_traces=200, offset=0)[source]#
Overlay successive symbol-length slices of a waveform to show its eye opening.
- Parameters:
signal (array_like) – Real baseband waveform sampled
samples_per_symboltimes per symbol.samples_per_symbol (int) – Oversampling factor, at least 2.
ax (matplotlib.axes.Axes, optional) – Axes to draw on; a new figure is created if omitted.
spans (int, optional) – Symbol periods per trace, at least 1.
max_traces (int, optional) – Largest number of traces drawn, at least 1.
offset (int, optional) – Samples skipped before the first trace, for example a filter delay that puts a sampling instant at the centre of the plot.
- Return type:
matplotlib.axes.Axes