PyTorch Radio¶
Additive White Gaussian Noise¶
PyTorch implementation of an AWGN wireless channel
-
class
rfml.ptradio.awgn.
AWGN
(snr: float = None)[source]¶ Additive White Gaussian Noise (AWGN) Channel model implemented in PyTorch.
The noise power is provided by SNR, which can be updated by calling set_snr. Each forward pass will have a different noise realization but the same SNR (as long as it has not been changed). This layer has no effect on sizes and can be made to be a pass through by setting SNR to None.
- Parameters
snr (float, optional) – Signal-to-Noise ratio. This can be overriden during operation by calling set_snr. Defaults to None.
Warning
This layer assumes that the average energy per symbol of the underlying signal is 1 (0 dB) when calculating the noise power.
This module makese no assumptions about the shape of the input and returns an identically shaped output.
-
forward
(x: torch.Tensor) → torch.Tensor[source]¶ Defines the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Module
instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.
Center Frequency Offset¶
PyTorch implementation of center/carrier frequency offset.
-
class
rfml.ptradio.cfo.
CFO
(cfo: float = 0.0)[source]¶ Center Frequency Offset Channel model implemented in PyTorch.
A center frequency offset receiver effect can be simulated by
\[s_{\text{rx}}(t) = e^{-j 2 \pi f_0 t} s_{\text{tx}}(t)\]Where \(f_0\) represents the normalized frequency offset in terms of the sample rate.
Further, a complex number, which \(s_{\text{tx}}\) is a vector of, is represented as
\[z = a + j b\]where \(a\) represents the real portion and \(b\) represents the imaginary portion of the number.
Due to Euler’s identity, a complex sine wave can be represented using
\[e^{-j 2 \pi f_o t} = \operatorname{cos}(2 \pi f_0 t) + j \operatorname{sin}(2 \pi f_0 t)\]Therefore,
\[ \begin{align}\begin{aligned}\begin{aligned} z_2 &= z_1 \times e^{-j 2 \pi f_0 t}\\ &= [a + jb] \times [\operatorname{cos}(2 \pi f_0 t) + j \operatorname{sin}(2 \pi f_0 t)]\\ &= a \operatorname{cos}(2 \pi f_0 t) + a j \operatorname{sin}(2 \pi f_0 t) + b j \operatorname{cos}(2 \pi f_0 t) + b j^2 \operatorname{sin}(2 \pi f_0 t)\\ &= a \operatorname{cos}(2 \pi f_0 t) + a j \operatorname{sin}(2 \pi f_0 t) + b j \operatorname{cos}(2 \pi f_0 t) - b \operatorname{sin}(2 \pi f_0 t)\\ &= [a \operatorname{cos}(2 \pi f_0 t) - b \operatorname{sin}(2 \pi f_0 t)] + j[a \operatorname{sin}(2 \pi f_0 t) + b \operatorname{cos}(2 \pi f_0 t)] \end{aligned}\end{aligned}\end{align} \]- Parameters
cfo (float, optional) – Center frequency offset percentage normalized to sample rate. This can be updated by calling set_cfo. Defaults to 0.0.
- Raises
ValueError – If the provided frequency offset is not in [-0.5, 0.5].
This module assumes that the input tensor is in BxCxIQxN format and returns a Tensor with the same dimensions.
-
forward
(x: torch.Tensor) → torch.Tensor[source]¶ Defines the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Module
instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.
Symbol Constellation¶
PyTorch implementation of (un)mapping symbol constellations.
-
class
rfml.ptradio.constellation.
ConstellationMapper
(constellation: numpy.ndarray)[source]¶ Layer that transforms “chunks” into symbols in the forward pass.
- Parameters
constellation (np.ndarray) – List of complex valued points in the constellation (2xM numpy array where M is the order of the modulation e.g. 2 for BPSK, 4 for QPSK, etc.)
- Raises
ValueError – If the constellation does not match the expected 2xM shape.
The forward pass should include Long Tensors that are in the half-open interval [0-M). The output of the forward pass will then be the symbol at that index in the constellation.
This module assumes that the input is [N] and extends the output to be of the format [BxCxIQxN] where B and C are 1, IQ is 2, and N matches the input.
-
forward
(x: torch.Tensor) → torch.Tensor[source]¶ Defines the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Module
instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.
-
class
rfml.ptradio.constellation.
ConstellationUnmapper
(constellation: numpy.ndarray)[source]¶ Layer that transforms symbols into “chunks” in the forward pass via a nearest neighbors algorithm.
- Parameters
constellation (np.ndarray) – List of complex valued points in the constellation (2xM numpy array where M is the order of the modulation e.g. 2 for BPSK, 4 for QPSK, etc.)
The forward pass should provide complex valued symbols and the output will be Long Tensors that are in the half-open interval [0-M).
This module assumes that the input is [BxCxIQxN] and assumes that B and C are both 1. Therefore, the output is provided as [N].
-
forward
(x)[source]¶ Defines the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Module
instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.
Downsample¶
PyTorch implementation of downsampling.
-
class
rfml.ptradio.downsample.
Downsample
(offset: int = 0, d: int = 8)[source]¶ PyTorch downsampling implementation.
- Parameters
offset (int, optional) – Transient samples at the beginning and end of the signal to leave off of the output (e.g. filter transients). Defaults to 0.
d (int, optional) – Decimation factor – only integer valued sample rate conversions are allowed. Defaults to 8.
- Raises
ValueError – If offset is less than 0.
ValueError – If d is less than 2.
This module assumes that the time dimension is 3 (e.g. [BxCxIQxN]). It returns a tensor in the same format, but, the output has been downsampled in the time dimension (e.g. [BxCxIQxN/D] ignoring any provided offsets).
-
forward
(x: torch.Tensor) → torch.Tensor[source]¶ Defines the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Module
instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.
Linear Modem¶
-
class
rfml.ptradio.modem.
Receiver
(modulation: str, alpha: float = 0.35, sps: int = 8, filter_span: int = 8)[source]¶ Class containing a full receive chain.
The basic chain structure can be described as:
[Baseband IQ Input]━━[RRC]━━[Downsample]━━[Constellation Unmapper]━━[Pack]━━[Output]
After being constructed, the user can call demodulate either with their own IQ data (shown as “Baseband IQ Input” above) and this data is assumed to be formatted as (1x1x2xN) which is the output of the Transmitter. The output of demodulate is then a list of bits (1s and 0s) that can be analyzed for bit error rate calculations or other applications.
- Parameters
modulation (str) – Modulation format to use. Currently supported values are: - BPSK - QPSK - 8PSK - QAM16 - QAM64
alpha (float, optional) – Roll-off factor for the RRC filter. Defaults to 0.35.
sps (int, optional) – Sample per symbol for Upsample. Defaults to 8.
filter_span (int, optional) – Half-sided RRC filter span in symbols. Defaults to 8.
- Raises
ValueError – If the constellation is unknown.
ValueError – If sps is not at least 2.
ValueError – If alpha is not in (0, 1).
ValueError – If filter span is not positive.
Warning
This receive chain is very simplistic and assumes both frame and symbol synchronization – in other words, its meant as a simple simulation toy and not as an actual receiver implementation.
-
demodulate
(iq: torch.Tensor) → List[int][source]¶ Demodulate a signal at baseband and return a list of bits (1s and 0s).
- Parameters
iq (torch.Tensor) – Complex baseband signal in (1x1x2xN) as output by Transmitter.modulate().
- Raises
ValueError – If the provided IQ does not have the shape (1x1x2xN).
- Returns
Demodulated bits.
- Return type
List[int]
-
class
rfml.ptradio.modem.
Transmitter
(modulation: str, alpha: float = 0.35, sps: int = 8, filter_span: int = 8)[source]¶ Class containing a full transmit chain.
The basic chain structure can be described as:
[Random]━┓ [User Input]━┻━[Unpack]━━[Constellation Mapper]━━[Upsample]━━[RRC]━━[Output]
After being constructed, the user can call modulate either with their own data (shown as “User Input” above) or they can pass in a set number of symbols in order to have “Random” data generated. Either way, the output is always IQ at baseband.
- Parameters
modulation (str) –
Linear modulation format to use. Currently supported values are:
BPSK
QPSK
8PSK
QAM16
QAM64
alpha (float, optional) – Roll-off factor for the RRC filter. Defaults to 0.35.
sps (int, optional) – Sample per symbol for Upsample. Defaults to 8.
filter_span (int, optional) – Half-sided RRC filter span in symbols. Defaults to 8.
- Raises
ValueError – If the constellation is unknown.
ValueError – If sps is not at least 2.
ValueError – If alpha is not in (0, 1).
ValueError – If filter span is not positive.
-
modulate
(bits: List[int] = None, n_symbols: int = 10000) → torch.Tensor[source]¶ Modulate a provided list of bits (1s and 0s) or random data of a set length.
If you wish to provide your own data, which is useful for later calculating the bit error rate, then you can directly pass in a list of bits that have been generated by your application (or randomly generated).
If instead you wish to simply get some quick data examples and don’t care about the underlying bit stream, then you can call this function with no arguments or override n_symbols to generate longer/shorter sequences.
- Parameters
bits (List[int], optional) – List of bits to modulate. Defaults to None.
n_symbols (int, optional) – Number of random symbols to generate. Defaults to 10000.
- Raises
ValueError – If bits is not provided and you set the number of symbols <= 0.
ValueError – If the bit stream would have to be zero padded for transmisison.
ValueError – If bits contains values other than just 1 and 0.
- Returns
Modulated data at baseband (1x1x2xn_symbols)
- Return type
torch.Tensor
-
rfml.ptradio.modem.
theoreticalBER
(snr: int, modulation: str) → float[source]¶ Lookup the theoretical BER for a given modulation scheme that has been precomputed using MATLAB’s berawgn.
- Parameters
snr (int) – Signal-to-Noise ratio (\(E_s/N_0\) not \(E_b/N_0\)).
modulation (str) –
Name of the modulation format – currently supported options are:
BSPK
QPSK
8PSK
QAM16
QAM64
- Raises
ValueError – If snr is not in [0-20].
ValueError – If modulation is unknown
- Returns
Theoretical BER @ snr (Es/N0) for modulation
- Return type
float
Note
MatLab takes in \(E_b/N_0\) for calculations as that is what is typically used and plotted in most literature. This code has chosen to use \(E_s/N_0\) instead which is easily related back to \(E_b/N_0\) through:
\[\frac{E_s}{N_0} = \frac{E_b}{N_0} - 10*log10(log2(M))\]Where \(M\) is the order of the modulation.
Root Raised Cosine (RRC) Filter¶
PyTorch implementation of a Root Raised Cosine filter
-
class
rfml.ptradio.rrc.
RRC
(alpha: float = 0.35, sps: int = 8, filter_span: int = 8, add_pad: bool = False)[source]¶ Root Raised Cosine filter implemented as a PyTorch convolution.
- Parameters
alpha (float, optional) – Roll-off factor of the filter. Defaults to 0.35.
sps (int, optional) – Samples per symbol. Defaults to 8.
filter_span (int, optional) – One-sided filter span in number of symbols. Defaults to 8.
add_pad (bool, optional) – True if padding should be added to simulate a tap delay. This should be True when this module is used as a pulse shaping filter and False when this module is used as a matched filter (because the extra data is useless anyways). Defaults to False.
- Raises
ValueError – If alpha is not in the interval (0.0, 1.0)
ValueError – If sps is not 2 or more
ValueError – If filter_span is not a positive integer
-
forward
(x: torch.Tensor) → torch.Tensor[source]¶ Defines the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Module
instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.
Signal Slicer¶
PyTorch implementation of an IQ Signal Slicer
-
class
rfml.ptradio.slicer.
Slicer
(width: int, offset: int = 0)[source]¶ Turn long continuous signals into discrete examples with a fixed width.
This can be thought of as batching up discrete examples to perform classification on in a real system. It starts at offset and creates as many examples as needed to fit all (though it will not create undersized examples so some may be thrown away) samples into discrete chunks. The examples are then concatenated in the batch dimension. The channel and IQ dimensions remain unchanged and naturally the time dimension will be identical to width.
This module is differentiable and can therefore be directly integrated in a training chain.
- Parameters
width (int) – Size of the examples or “number of samples” in the time dimension.
offset (int, optional) – Number of samples to skip at the beginning and end. This can be useful for ignoring filter transients on the sides where the data is unusable. Defaults to 0.
- Raises
ValueError – If width is not a positive integer.
ValueError – If offset is negative.
This module assumes that the input is formatted as BxCxIQxT. The returned output from the forward pass will have a large batch dimension and the time dimension will match the width provided. The other dimensions are left unchanged.
-
forward
(x: torch.Tensor) → torch.Tensor[source]¶ Defines the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Module
instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.
Upsample¶
PyTorch implementation of upsampling (inserting <i-1> zeros).
-
class
rfml.ptradio.upsample.
Upsample
(i: int = 8)[source]¶ PyTorch upsampling implementation.
This module upsamples by inserting <i-1> zeros in between samples in the time dimension. It does not low pass filter after this and assumes that the filter is a separate module if desired.
See also
rfml.ptradio.RRC
- Parameters
i (int, optional) – Interpolation factor – only integer valued sample rate conversions are allowed. Defaults to 8.
- Raises
ValueError – If i is less than 2.
This module assumes that the input is formatted as BxCxIQxT. The time dimension is extended by a factor of i, as provided, and all other dimensions are untouched.
-
forward
(x: torch.Tensor) → torch.Tensor[source]¶ Defines the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Module
instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.