The receiver_hybrid object captures reception from a hybrid digital/analog combining receiver. In addition to that of the receiver object, a receiver_hybrid object is responsible for achieving reception (i.e., estimating a symbol vector) via the combination of digital and analog combining (rather than solely digital combining like in the receiver object).

The $N_{\mathrm{s}} \times 1$ received symbol vector $\hat{\mathbf{s}}$ after the hybrid receiver can be written as

where $\mathbf{y}$ is the $N_{\mathrm{r}} \times 1$ complex received signal vector at the receive array, $\mathbf{W}_{\mathrm{RF}}$ is the $N_{\mathrm{r}} \times L_{\mathrm{r}}$ analog combining matrix, and $\mathbf{W}_{\mathrm{BB}}$ is the $L_{\mathrm{r}} \times N_{\mathrm{s}}$ digital combining matrix.

Digital combining—represented by the matrix $\mathbf{W}_{\mathrm{BB}}$—takes place in the digital domain at baseband (hence “BB”).

Analog combining—represented by the matrix $\mathbf{W}_{\mathrm{RF}}$—takes place in the analog domain via a network of phase shifters and possibly attenuators at radio frequency (hence “RF”).

This staged approach to combining is common for practical transceivers with many antennas (e.g., massive MIMO, millimeter-wave communication) to reduce the number of costly and power hungry RF chains while still supplying high beamforming gains and supporting spatial multiplexing.

The received signal vector is mixed with the analog combining matrix before being digitized after each of the $L_{\mathrm{r}}$ RF chains. In order to support spatial multiplexing of $N_{\mathrm{s}}$ streams, it should be such that

The signals from each antenna are distributed throughout the analog combining network before being passed to each of the RF chains—how this is done varies depending on the architecture.

### Creating a Hybrid Digital/Analog Receiver

To create a hybrid digital/analog receiver, a convenient way is to use

rx = receiver.create('hybrid')


This will instantiate a receiver_hybrid object, which is inherited from the receiver object definition.

As such, the receiver_hybrid object inherits all properties and methods of the receiver object. However, some methods are overwritten in the receiver_hybrid object definition to appropriately accommodate the nature of hybrid digital/analog beamforming.

### Key Properties

A receiver_hybrid object inherits all properties of the receiver object. We summarize the key properties of such as follows; please see documentation on the receiver object for more details.

rx.combiner
rx.noise
rx.noise_energy_per_symbol
rx.noise_power_per_Hz
rx.noise_covariance
rx.symbol_bandwidth
rx.symbol_period
rx.array


Naturally, the biggest difference between a fully-digital receiver object and a hybrid digital/analog receiver_hybrid object lay in combining. Recall, however, the effective combining matrix of a hybrid digital/analog receiver is the product of its digital and analog combiners.

To capture the digital combining matrix $\mathbf{W}_{\mathrm{BB}}$ and the analog combining matrix $\mathbf{W}_{\mathrm{RF}}$, the receiver_hybrid object has the following properties

rx.combiner_digital
rx.combiner_analog


Their product $\mathbf{W}_{\mathrm{RF}} \mathbf{W}_{\mathrm{BB}}$ is captured by the inherited property

rx.combiner


from the receiver object.

Capturing the number of RF chains at the hybrid receiver is the property

rx.num_rf_chains


It is common for practical analog beamforming networks to be controlled digitally using limited-resolution phase shifters and attenuators. To capture this, MFM’s receiver_hybrid object has the following properties

rx.combiner_analog_phase_resolution_bits
rx.combiner_analog_amplitude_resolution_bits
rx.combiner_analog_amplitude_quantization_law


As their names suggest rx.combiner_analog_phase_resolution_bits and rx.combiner_analog_amplitude_resolution_bits are the number of bits used to control each phase shifter and each attenuator, respectively. The property rx.combiner_analog_amplitude_quantization_law provides some freedom in how MFM captures finite-resolution attenuators. We discuss more on phase and amplitude control in Limited Phase Control and Limited Amplitude Control.

### Limited Phase Control

To set the number of bits used to configure each phase shifter in the analog beamforming network to bits, use

rx.set_combiner_analog_amplitude_resolution_bits(bits)


MFM assumes uniform phase shifter quantization, meaning the phases it can implement are on the range $[0,2\pi)$ with step size $2\pi/2^{b_{\mathrm{phs}}}$, where each phase shifter has $b_{\mathrm{phs}}$ bits of phase control.

To check to see if an analog combining matrix W_RF satisfies the phase contraints of the hybrid receiver’s analog beamforming network, one can use

out = check_combiner_analog_phase_constraint(W_RF)


where out is true if satisfied and false if any entries of W_RF violate the phase constraints.

It is also common in academic research to assume phase shifters can take on any value to remove the impact of having limited phase resolution. To realize this in MFM, simply use

rx.set_combiner_analog_amplitude_resolution_bits(Inf)


This removes any phase constraint on the entries of the analog combiner.

### Limited Amplitude Control

To set the number of bits used to configure each attenuator in the analog beamforming network to bits, use

rx.set_combiner_analog_amplitude_resolution_bits(bits)


To capture the case where there are no attenuators in the analog combining network, use

rx.set_combiner_analog_amplitude_resolution_bits(0)


which will impose a unit amplitude constraint on the entries of the analog combining matrix.

To capture the case where the attenuators are not faced with amplitude quantization, use

rx.set_combiner_analog_amplitude_resolution_bits(Inf)


which allows the magnitude of the entries of the analog combining matrix to take on any value between $[0,1]$.

In a similar fashion, MFM assumes uniform attenuator quantization offers users two options: linear quantization or logarithmic quantization.

### Connected-ness of the Hybrid Receiver

Fully-connected architectures, for example, supply each receive antenna with a weighted version of each RF chain’s signal. In other words, each RF chain is connected to each antenna.

Partially-connected architectures, on the other hand, only supply certain receive antennas (often groups of antennas) with a weighted version of the signals from certain RF chains.

The connected-ness of a hybrid digital/analog architecture is reflected by the structure of the analog combining matrix $\mathbf{W}_{\mathrm{RF}}$. The $(i,j)$-th entry is only nonzero if there exists a weighted connection between the $i$-th antenna and the $j$-th RF chain.

To set which RF chains are connected to which antennas, MFM supplies the function

rx.set_combiner_hybrid_connections(M)


where M is an $N_{\mathrm{r}} \times L_{\mathrm{r}}$ matrix (like the analog combining matrix) whose $(i,j)$-th entry is 1 if there exists a weighted connection between the $i$-th antenna and the $j$-th RF chain and 0 if not.

Fully-connected architectures, which are assumed by default, have an M matrix of all ones.

Sub-array architectures, for example, have an M matrix with a block diagonal of ones.

### Example Setup

A typical hybrid digital/analog receiver setup looks something similar to

rx = receiver.create('hybrid')
rx.set_symbol_bandwidth(B)
rx.set_num_streams(Ns)
rx.set_set_array(a)
rx.set_noise_power_per_Hz(noise_psd,'dBm_Hz')


where B is the bandwidth in Hz, Ns is the number of streams to multiplex, a is an array object, noise_psd is the noise power per Hz in dBm/Hz (e.g., -174).

After this initial setup, the receiver rx can be configured to receive symbol vectors using its combiner.

### Setting the Digital Combiner

The most straightforward way to set the digital combining matrix is to use

rx.set_combiner_digital(W_BB)


where W_BB is an $L_{\mathrm{r}} \times N_{\mathrm{s}}$ combining matrix; if the combining matrix is not of appropriate size based on the current number of RF chains and number of streams, MFM will alert the user and it will not set the digital combiner.

### Setting the Analog Combiner

The most straightforward way to set the analog combining matrix is to use

rx.set_combiner_analog(W_RF)


where W_RF is an $N_{\mathrm{r}} \times L_{\mathrm{r}}$ combining matrix; if the combining matrix is not of appropriate size based on the current number of antennas and number of RF chains, MFM will alert the user and it will not set the analog combiner.

Additionally, MFM will require that W_RF satisfy the analog combining constraints associated with phase and amplitude control.

### Shorthand Methods

To provide convenient ways to retrieve common MIMO-related quantities from a receiver_hybrid object rx, there exist the following so-called shorthand methods.

• rx.W — Returns the combining matrix.
• rx.W_RF — Returns the analog combining matrix.
• rx.W_BB — Returns the digital combining matrix.
• rx.Ns — Returns the number of streams.
• rx.Lr — Returns the number of RF chains.
• rx.Nr — Returns the number of receive antennas.
• rx.s — Returns the receive symbol.
• rx.y — Returns the signal vector arriving at the receiver.

### List of Properties

The receiver_hybrid object contains the following properties:

• receiver_hybrid.num_rf_chains
• receiver_hybrid.combiner_digital
• receiver_hybrid.combiner_analog
• receiver_hybrid.combiner_analog_phase_resolution_bits
• receiver_hybrid.combiner_analog_amplitude_resolution_bits
• receiver_hybrid.combiner_analog_amplitude_quantization_law
• receiver_hybrid.combiner_hybrid_connections
• receiver_hybrid.name
• receiver_hybrid.type
• receiver_hybrid.array
• receiver_hybrid.num_antennas
• receiver_hybrid.num_streams
• receiver_hybrid.symbol_bandwidth
• receiver_hybrid.symbol_period
• receiver_hybrid.combiner
• receiver_hybrid.noise
• receiver_hybrid.received_signal
• receiver_hybrid.receive_symbol
• receiver_hybrid.channel_state_information
• receiver_hybrid.noise_power
• receiver_hybrid.noise_energy_per_symbol
• receiver_hybrid.noise_power_per_Hz
• receiver_hybrid.noise_covariance
• receiver_hybrid.receive_strategy
• receiver_hybrid.source

### List of Methods

The receiver_hybrid object contains the following methods:

### Methods Documentation

#### N0()

Returns the noise energy per symbol.

Usage:
val = N0()
Return Values:
val — the noise energy per symbol

Back to methods

#### Nr()

Returns the number of receive antennas.

Usage:
val = Nr()
Return Values:
val — the number of receive antennas

Back to methods

#### Ns()

Returns the number of streams.

Usage:
val = Ns()
Return Values:
val — the number of streams

Back to methods

#### Rn()

Returns the noise covariance matrix.

Usage:
val = Rn()
Return Values:
val — the noise covariance matrix

Back to methods

#### W()

Returns the combining matrix.

Usage:
val = W()
Return Values:
val — the combining matrix

Back to methods

#### check_combiner_analog_amplitude_constraint()

Checks if the analog combiner has entries whose amplitudes have been quantized to the appropriate resolution.

Usage:
out = check_combiner_analog_amplitude_constraint()
Return Values:
out — a boolean indicating whether or not the analog amplitude constraint has been met

Back to methods

#### check_combiner_analog_dimensions()

Checks to see if the analog combining matrix has dimensions appropriate for the current number of RF chains and antennas.

Usage:
out = check_combiner_analog_dimensions()
Return Values:
out — a boolean indicating if the analog combiner is of appropriate dimension

Back to methods

#### check_combiner_analog_phase_constraint()

Checks if the analog combiner has entries whose phases have been quantized to the appropriate resolution.

Usage:
out = check_combiner_analog_phase_constraint()
Return Values:
out — a boolean indicating whether or not the analog phase constraint has been met

Back to methods

#### check_combiner_digital_dimensions()

Checks to see if the digital combining matrix has dimensions appropriate for the current number of RF chains and streams.

Usage:
out = check_combiner_digital_dimensions()
Return Values:
out — a boolean indicating if the digital combiner is of appropriate dimension

Back to methods

#### check_combiner_dimensions()

Checks to see if the analog and digital combiners have compatible dimensions.

Usage:
out = check_combiner_dimensions()
Return Values:
out — a boolean indicating if the analog and digital combiners have compatible dimensions.

Back to methods

#### check_receive_strategy(strategy)

Returns a boolean indicating if a receive strategy is valid.

Usage:
out = check_receive_strategy(strategy)
Input Arguments:
strategy — a string indentifying a specific receive strategy
Return Values:
out — a boolean

Back to methods

#### configure_receiver(strategy)

Configures the receiver’s combiner according to the current/specified receive strategy, incorporating channel state information as applicable.

Usage:
configure_receiver()
configure_receiver(strategy)
Input Arguments:
strategy — (optional) receive strategy to use; if not passed, the current receive strategy will be used

Back to methods

#### configure_receiver_eigen()

Configures the receiver to receive along the strongest components of the desired channel, neglecting interference, etc.

Usage:
configure_receiver_eigen()

Back to methods

#### configure_receiver_identity()

Configures the receive combiner to an identity matrix.

Usage:
configure_receiver_identity()

Back to methods

#### configure_receiver_mmse_desired()

Configures the receiver in an LMMSE fashion to reject noise. Does not reject interference.

Usage:
configure_receiver_mmse_desired()

Back to methods

#### configure_receiver_mmse_interference()

Configures the receiver in an LMMSE fashion to reject noise and interference.

Usage:
configure_receiver_mmse_interference()

Back to methods

#### create(type)

Usage:
obj = receiver.create()
obj = receiver.create(type)
Input Arguments:
type — (optional) a string specifying the type of receiver to create; either ‘digital’ or ‘hybrid’
Return Values:
obj — a receiver object

Back to methods

#### enforce_combiner_power_budget()

Ensures that the digital combiner is normalized such that the total combining power budget is satisfied.

Usage:
enforce_combiner_power_budget()
Notes:
Supersedes the function with the same name in the parent.

Back to methods

#### get_receive_symbol()

Returns the receive symbol based on the curent received signal vector, noise, and combiner.

Usage:
s = get_receive_symbol()
Return Values:
s — the receive symbol

Back to methods

#### get_source_channel_state_information_index()

Returns the index of the receive CSI entry whose transmit device corresponds to the receiver’s source device.

Usage:
idx = get_source_channel_state_information_index()
Return Values:
idx — an index; if zero, the CSI entry was not found

Back to methods

#### get_valid_receive_strategies()

Returns a cell containing all valid receive strategy strings.

Usage:
out = get_valid_receive_strategies()
Return Values:
out — a cell of strings
Notes:
This function will differ between a fully-digital receiver and hybrid digital/analog receiver to account for receive strategies that are specific to each.
This function will need to be updated anytime a custom/new receive strategy is added.
The strings should be all lowercase.

Back to methods

#### hybrid_approximation(F,opt)

Performs hybrid approximation of a fully-digital beamforming matrix based on the hybrid approximation method and any associated options.

Usage:
[X,RF,BB,err] = hybrid_approximation(F,opt)
Input Arguments:
F — fully-digital combining matrix
opt — struct of options specific to the hybrid approximation method
Return Values:
X — the effective fully-digital beamforming matrix
RF — the resulting analog beamforming matrix
BB — the resulting digital beamforming matrix
err — the squared Frobenius norm of the error in hyrbrid approximation
Notes:
The analog and digital combiners are set after approximation.

Back to methods

#### impose_beamformer_power_constraint()

Imposes the power constraint to the digital beamformer such that each stream’s beamformer has Frobenius norm of square root of the number of transmit antennas.

Usage:
IMPOSE_DIGITAL_BEAMFORMER_POWER_CONSTRAINT()

Back to methods

#### initialize()

Usage:
initialize()

Back to methods

#### n()

Returns the noise vector (per-antenna).

Usage:
val = n()
Return Values:
val — the noise vector (per-antenna)

Back to methods

#### omp_based_hybrid_approximation(A,B,C,Nrf,P)

Input Arguments:
A — predefined RF beamforming vectors as columns (codebook)
B — desired fully-digital beamformer
C — identity of size Nr by Nr (eye(Nr)) (covariance matrix E[yy*]?)
Nrf — number of RF chains
P — power constraint (optional)

Back to methods

#### parse_channel_state_information()

Splits the CSI into two components: (i) a struct containing CSI for receiving from the source and (ii) a cell of structs containing CSI for all other links (e.g., interference channels).

Usage:
[csi_des,csi_int] = parse_channel_state_information()
Return Values:
csi_des — a CSI struct
csi_int — a cell of one or more CSI structs

Back to methods

Back to methods

#### s()

Usage:
val = s()
Return Values:
val — the receive symbol vector

Back to methods

#### set_array(array)

Usage:
set_array(array)
Input Arguments:
array — an array object
Notes:

Back to methods

#### set_channel_state_information(csi)

Sets the receive channel state information (CSI).

Usage:
set_channel_state_information(csi)
Input Arguments:
csi — a cell of channel state information structs

Back to methods

#### set_combiner(W)

Usage:
set_combiner(W)
Input Arguments:
W — receive combining matrix

Back to methods

#### set_combiner_analog(W)

Sets the analog combining matrix.

Usage:
set_combiner_analog(W)
Input Arguments:
W — analog combining matrix
Notes:
Also updates the effective combining matrix.

Back to methods

#### set_combiner_analog_amplitude_quantization_law(law)

Sets the analog combiner’s amlitude quantization law (e.g., linear or log).

Usage:
set_combiner_analog_amplitude_quantization_law(law)
Input Arguments:
law — either ‘linear’ for uniform linear amplitude quantization between (0,1]; or a negative number corresponding to the (amplitude) attenuation step size in dB
Notes:
A law = -0.25 represents an analog combiner with attenuators having a step size of -0.25 dB, meaning with 3 bits of amplitude resolution, for example, the attenuators can attenuate at most -0.25 dB * 2^3 = -2 dB. Practical attenuators often are stepped in such a log-scale, motivating us to support both linear- and log-based amplitude quantization laws.

Back to methods

#### set_combiner_analog_amplitude_resolution_bits(bits)

Sets the resolution (in bits) of the attenuators in the analog combiner.

Usage:
set_combiner_analog_amplitude_resolution_bits(bits)
Input Arguments:
bits — number of bits of amplitude control
Notes:
When bits = Inf, amplitudes of analog beamforming weights can take on any value

Back to methods

#### set_combiner_analog_phase_resolution_bits(bits)

Sets the resolution (in bits) of the phase shifters in the analog combiner.

Usage:
set_combiner_analog_phase_resolution_bits(bits)
Input Arguments:
bits — number of bits of phase control

Back to methods

#### set_combiner_digital(W)

Sets the digital combiner.

Usage:
set_combiner_digital(W)
Input Arguments:
W — digital combining matrix

Back to methods

#### set_hybrid_approximation_method(method)

Sets the method to use during hybrid approximation of a fully-digital beamformer.

Usage:
set_hybrid_approximation_method(method)
Input Arguments:
method — a string declaring which method to use

Back to methods

#### set_name(name)

Sets the name of the receiver.

Usage:
set_name()
set_name(name)
Input Arguments:
name — (optional) a string; if not passed, ‘receiver’ is the default name used

Back to methods

#### set_noise(n)

Sets the noise vector at the receive array.

Usage:
set_noise()
set_noise(n)
Input Arguments:
n — (optional) a noise vector; if not passed, noise will be generated according to the noise power

Back to methods

#### set_noise_covariance(Rn)

Sets the noise covariance matrix.

Usage:
set_noise_covariance(Rn)
Input Arguments:
Rn — noise covariance matrix

Back to methods

#### set_noise_power_per_Hz(noise_psd,unit)

Sets the noise power per Hz based for a given noise power spectral density.

Usage:
set_noise_power_per_Hz(noise_psd)
set_noise_power_per_Hz(noise_psd,unit)
Input Arguments:
noise_psd — noise power spectral density (power per Hz) (e.g., -174 dBm/Hz)
unit — an optional unit specifying the units of noise_psd (e.g., ‘dBm_Hz’ for dBm/Hz (default) or ‘watts_Hz’ for watts/Hz)
Notes:
Also updates the noise power and noise covariance.

Back to methods

#### set_num_rf_chains(L)

Sets the number of RF chains in the receiver’s hybrid beamforming structure.

Usage:
set_num_rf_chains(L)
Input Arguments:
L — number of RF chains

Back to methods

#### set_num_streams(Ns)

Sets the number of data streams being received.

Usage:
set_num_streams(Ns)
Input Arguments:
Ns — number of data streams

Back to methods

#### set_receive_strategy(strategy)

Usage:
set_receive_strategy()
set_receive_strategy(strategy)
Input Arguments:
strategy — (optional) a string specifying the receive strategy; options vary depending on receiver type (digital vs. hybrid); if not passed, identity reception will be used

Back to methods

#### set_receive_symbol(s)

Usage:
set_receive_symbol(s)
Input Arguments:
s — the receive symbol (e.g., after combiner)

Back to methods

#### set_received_signal(y)

Usage:
set_received_signal(y)
Input Arguments:
y — the received signal vector (i.e., vector impinging the receive array)

Back to methods

#### set_source(device_source)

Sets the source (device the receiver aims to serve).

Usage:
set_source(device_source)
Input Arguments:
device_source — a device object

Back to methods

#### set_symbol_bandwidth(B)

Usage:
set_symbol_bandwidth(B)
Input Arguments:
B — symbol bandwidth in Hertz
Notes:
Also updates the symbol period and noise power.

Back to methods

#### set_type(type)

Sets the type of the receiver.

Usage:
set_type()
set_type(type)
Input Arguments:
type — (optional) a string; if not passed, ‘digital’ is the default type used

Back to methods

#### show_analog_beamformer(idx)

Plots one of the analog beamformers.

Usage:
show_analog_beamformer()
show_analog_beamformer(idx)
Input Arguments:
idx — an index specifying which beamformer in the analog combining matrix to plot (default of 1)

Back to methods

#### show_effective_beamformer(idx)

Plots one of the effective beamformers (combination of analog and digital).

Usage:
show_effective_beamformer()
show_effective_beamformer(idx)
Input Arguments:
idx — an index specifying which beamformer in the effective combining matrix to plot (default of 1)

Back to methods

#### turn_off()

Turns off the receiver by setting its combiner to a zeros matrix.

Usage:
turn_off()

Back to methods

#### update_combiner()

Updates the effective combining matrix based on the current analog and digital combiners. Only updates if the dimensionality supports the matrix multiply.

Usage:
update_combiner()

Back to methods

#### update_noise_covariance()

Updates the noise covariance matrix based on the current number of antennas and noise power.

Usage:
update_noise_covariance()
Notes:
We have assumed i.i.d. noise across antennas.

Back to methods

#### update_noise_power()

Updates the noise power based on the current noise power per Hz and symbol bandwidth.

Usage:
update_noise_power()
Notes:
Also updates the noise covariance matrix.
As of now (Jan. 12, 2021), noise per symbol is equal to the noise power per Hz. Changes to notation or future accommodations may change how we define these two quantities so we are leaving them both present for the time being.

Back to methods

#### update_receive_symbol()

Usage:
update_receive_symbol()
Notes:
Should be called when the combiner, received signal, or noise are changed.

Back to methods

#### y()

val = y()
val — the received signal vector