Null Steering Beamformer

Inheritance diagram of hermespy.beamforming.nullsteeringbeamformer

Null-steering [1] transmit beamformers aim to maximize the received signal power in the direction of the intended receiver while substantially reducing the power impinging on the unintended receivers located in other directions.

Let us introduce

\[\mathbf{a}_{\psi_{\bullet}} \triangleq \left[e^{j \frac{2\pi}{\lambda} r_1 \cos(\phi - \psi_1)}, \dots, e^{j \frac{2\pi}{\lambda} r_K \cos(\phi - \psi_K)} \right]^T\]

and

\[\mathbf{w} \triangleq \left[ w_1 \dots w_K \right]^T\]

Where \(\mathbf{a}_{\psi_{\bullet}}\) is the array propagation matrix and the vector \(W\) corresponds to the beamforming weights. Let

\[\mathbf{A} \triangleq \left[ \mathbf{a}_{\phi_1} \dots \mathbf{a}_{\phi_L} \right]\]

where \(A\) is the matrix that contains the array propagation vectors of all AoAs and \(P_T\) denote the maximum admissible total transmission power. The beamformer has maximized power in the intended direction and nulls at the unintended directions when:

\[\begin{split}\max_{\mathbf{w}} & \quad |\mathbf{w}^H \mathbf{a}_0|^2 \\ \text{subject to} & \quad \mathbf{w}^H \mathbf{A} = 0 \\ & \quad \mathbf{w}^H \mathbf{w} \leq P_T.\end{split}\]

The optimal solution to the above expression can be derived as:

\[\mathbf{w}_{\text{ns}} = \frac{\sqrt{P_T}}{\|(\mathbf{I} - \mathbf{P_A})\mathbf{a}_0\|} \cdot (\mathbf{I} - \mathbf{P_A})\mathbf{a}_0\]

where

\[\mathbf{P_A} \triangleq \mathbf{A}(\mathbf{A}^H\mathbf{A})^{-1}\mathbf{A}^H\]

is the orthogonal projection matrix onto the subspace spanned by the columns of \(A\). As such, \(W_{\text{ns}}\) is in fact the orthogonal projection of onto the null space of \(a_0\). Thus, \(W_{\text{ns}}\) is the null steering beamformer.

The radiation pattern of a NullSteeringBeamformer can be visualised using the following code snippet. A uniform array is ctreated and the NullSteeringBeamformer is assigned to the array. The radiation pattern can be visualised by specifiying the beamformer’s transmitting focus point and calling the plot_pattern method.

 1# Configure a uniform antenna array
 2from hermespy.simulation import SimulatedIdealAntenna, SimulatedUniformArray
 3
 4uniform_array = SimulatedUniformArray(SimulatedIdealAntenna, half_wavelength, (8, 8, 1))
 5
 6# Configure beamformer
 7from hermespy.beamforming.nullsteeringbeamformer import NullSteeringBeamformer
 8from hermespy.beamforming import SphericalFocus
 9
10beamformer = NullSteeringBeamformer()
11beamformer.transmit_focus = [SphericalFocus(0,0), SphericalFocus(-.5*pi,.25*pi),SphericalFocus(.5*pi,.25*pi)]
12
13# Render the beamformer's characteristics
14import matplotlib.pyplot as plt
15
16_ = uniform_array.plot_pattern(carrier_frequency, beamformer, title="Null Steering Beamformer")
17plt.show()
../../_images/nullsteeringbeamformer_radiation_pattern.png

The following section provides an example for the usage of the nullsteeringbeamformer in a communication scenario.

For this a new simulation environment is initialised. A digital antenna array consisting of ideal isotropic antenna elements spaced at half wavelength intervals is created and is assigned to a device representing a base station.

1base_station_device = simulation.new_device(
2    antennas=SimulatedUniformArray(SimulatedIdealAntenna, .5 * wavelength, (5, 5, 1)),
3    carrier_frequency=carrier_frequency,
4    pose=Transformation.From_Translation(np.array([0, 0, 0])),
5)

To probe the characterestics a cosine waveform is generated as the transmit waveform.

1waveform = RootRaisedCosineWaveform(
2    num_preamble_symbols=32,
3    num_data_symbols=128,
4    roll_off=.9,
5)

The base station device is configured to transmit the beamformed signal by assigning the NullSteeringBeamformer to the base station device.

1beamformer = NullSteeringBeamformer()
2base_station_device.transmit_coding[0] = beamformer
3
4base_station_transmitter = TransmittingModem(waveform=deepcopy(waveform))
5base_station_device.add_dsp(base_station_transmitter)

Now the simulation can be extended to evalaute the performance in a real world communication scenario. For this three devices representing the UEs are added, to be illuminated by the BS.

 1user_equipment_device_1 = simulation.new_device(
 2    carrier_frequency=carrier_frequency,
 3    pose=Transformation.From_Translation(np.array([200., 200., 100.])),
 4)
 5user_equipment_device_2 = simulation.new_device(
 6    carrier_frequency=carrier_frequency,
 7    pose=Transformation.From_Translation(np.array([200., -200., 100.])),
 8)
 9user_equipment_device_3 = simulation.new_device(
10    carrier_frequency=carrier_frequency,
11    pose=Transformation.From_Translation(np.array([-400., 200., 100.])),
12)

The user equipments can be configured to receive the signal by setting them up as receiving modems.

1ue_receiver_1 = ReceivingModem(waveform=deepcopy(waveform))
2ue_receiver_2 = ReceivingModem(waveform=deepcopy(waveform))
3ue_receiver_3 = ReceivingModem(waveform=deepcopy(waveform))
4user_equipment_device_1.add_dsp(ue_receiver_1)
5user_equipment_device_2.add_dsp(ue_receiver_2)
6user_equipment_device_3.add_dsp(ue_receiver_3)

Now as defined the nullsteeringbeamformer illuminates the maximum radiation on one UE whereas the nulls on the others. This can be realised by configuring the transmit foucs of the Beamformer.

1beamformer.transmit_focus = [
2    DeviceFocus(user_equipment_device_1),  # Focus on UE1
3    DeviceFocus(user_equipment_device_2),  # Null on UE2
4    DeviceFocus(user_equipment_device_3),  # Null on UE3
5]

The propgation characterestics between the BS and the UEs can be modelled using the SpatialDelayChannel Model.

 1simulation.set_channel(
 2    base_station_device,
 3    user_equipment_device_1,
 4    SpatialDelayChannel(model_propagation_loss=False),
 5)
 6
 7simulation.set_channel(
 8    base_station_device,
 9    user_equipment_device_2,
10    SpatialDelayChannel(model_propagation_loss=False),
11)
12
13simulation.set_channel(
14    base_station_device,
15    user_equipment_device_3,
16    SpatialDelayChannel(model_propagation_loss=False),
17)

Now hermespy can be instructed to conduct a simulation campaign to evaluate the received signal power at the UEs by adding the evaluator.

 1simulation.add_evaluator(ReceivePowerEvaluator(ue_receiver_1))
 2simulation.add_evaluator(ReceivePowerEvaluator(ue_receiver_2))
 3simulation.add_evaluator(ReceivePowerEvaluator(ue_receiver_3))
 4
 5# Creating a new dimension to dynamically switch the focus of the beamformer during the simulation campaign.
 6simulation.new_dimension(
 7    'transmit_focus',
 8    [
 9        [DeviceFocus(user_equipment_device_1), DeviceFocus(user_equipment_device_2), DeviceFocus(user_equipment_device_3)],
10        [DeviceFocus(user_equipment_device_2), DeviceFocus(user_equipment_device_3), DeviceFocus(user_equipment_device_1)],
11        [DeviceFocus(user_equipment_device_3), DeviceFocus(user_equipment_device_1), DeviceFocus(user_equipment_device_2)],
12    ],
13    beamformer
14)
15result = simulation.run()

In the previous example the NullSteeringBeamformer was assigned to transmitting base station. The same example can be adpated to study the performance of the beamformer assigned to a receiving base station.

The base station is configured for reception.

1beamformer = NullSteeringBeamformer()
2base_station_device.receive_coding[0] = beamformer
3
4base_station_receiver = ReceivingModem(waveform=deepcopy(waveform))
5base_station_device.add_dsp(base_station_receiver)

The UEs will be configured as transmitting.

1user_equipment_transmitter_1 = TransmittingModem(waveform=deepcopy(waveform))
2user_equipment_transmitter_2 = TransmittingModem(waveform=deepcopy(waveform))
3user_equipment_transmitter_3 = TransmittingModem(waveform=deepcopy(waveform))
4user_equipment_device_1.add_dsp(user_equipment_transmitter_1)
5user_equipment_device_2.add_dsp(user_equipment_transmitter_2)
6user_equipment_device_3.add_dsp(user_equipment_transmitter_3)

The receieve focus of the beamformer will be configured.

1beamformer.receive_focus = [
2    DeviceFocus(user_equipment_device_1),  # Focus on User Equipmment 1
3    DeviceFocus(user_equipment_device_2),  # Null on User Equipmment 2
4    DeviceFocus(user_equipment_device_3),  # Null on User Equipmment 3
5]

The performance of the beamformer is studied by analysing the received signal quality from the respective UEs. For this purpose, the Error Vector Magnitude of the consetallation diagram of the Received signal is evaluated.

 1simulation.add_evaluator(ConstellationEVM(user_equipment_transmitter_1, base_station_receiver))
 2simulation.add_evaluator(ConstellationEVM(user_equipment_transmitter_2, base_station_receiver))
 3simulation.add_evaluator(ConstellationEVM(user_equipment_transmitter_3, base_station_receiver))
 4
 5# Creating a new dimension to dynamically switch the focus of the beamformer during the simulation campaign.
 6simulation.new_dimension(
 7    'receive_focus',
 8    [
 9        [DeviceFocus(user_equipment_device_1), DeviceFocus(user_equipment_device_2), DeviceFocus(user_equipment_device_3)],
10        [DeviceFocus(user_equipment_device_2), DeviceFocus(user_equipment_device_3), DeviceFocus(user_equipment_device_1)],
11        [DeviceFocus(user_equipment_device_3), DeviceFocus(user_equipment_device_1), DeviceFocus(user_equipment_device_2)],
12    ],
13    beamformer
14)
15result = simulation.run()
class NullSteeringBeamformer[source]

Bases: TransmitBeamformer, ReceiveBeamformer

classmethod Deserialize(process)[source]

Deserialize an object’s state.

Objects cannot be deserialized directly, instead a Factory must be instructed to carry out the deserialization process.

Parameters:

process (DeserializationProcess) – The current stage of the deserialization process. This object is generated by the Factory and provides an interface to deserialization methods supporting multiple backends.

Return type:

NullSteeringBeamformer

Returns:

The deserialized object.

_decode(samples, carrier_frequency, angles, array)[source]

Decode signal streams for receive beamforming.

This method is called as a subroutine during decode_streams and probe.

Parameters:
  • samples (ndarray) – Signal samples, first dimension being the number of signal streams \(N\), second the number of samples \(T\).

  • carrier_frequency (float) – The assumed carrier central frequency of the samples \(f_\mathrm{c}\).

  • angles (ndarray) – Spherical coordinate system angles of arrival in radians. A three-dimensional numpy array with the first dimension representing the number of angles, the second dimension of magnitude number of focus points \(F\), and the third dimension containing the azimuth and zenith angle in radians, respectively.

  • array (AntennaArrayState) – The assumed antenna array.

Return type:

ndarray

Returns:

Stream samples of the focused signal towards all focus points. A three-dimensional numpy array with the first dimension representing the number of focus points, the second dimension the number of returned streams and the third dimension the amount of samples.

_encode(samples, carrier_frequency, focus_angles, array)[source]

Encode signal streams for transmit beamforming.

Parameters:
  • samples (ndarray) – Signal samples, first dimension being the number of transmit antennas, second the number of samples.

  • carrier_frequency (float) – The assumed central carrier frequency of the samples generated RF signal after mixing in Hz.

  • focus_angles (ndarray) – Focused angles of departure in radians. Two-dimensional numpy array with the first dimension representing the number of focus points and the second dimension of magnitude two being the azimuth and elevation angles, respectively.

  • array (AntennaArrayState) – The assumed antenna array.

Return type:

ndarray

Returns:

The encoded signal samples. Two-dimensional complex-valued numpy array with the first dimension representing the number of transmit antennas streams and the second dimension the number of samples.

num_receive_output_streams(num_input_streams)[source]

Get required number of output streams during decoding.

Parameters:

num_input_streams (int) – Number of input streams.

Return type:

int

Returns: The number of output streams. Negative numbers indicate infeasible configurations.

num_transmit_input_streams(num_output_streams)[source]

Get required number of input streams during encoding.

Parameters:

num_output_streams (int) – Number of desired output streams.

Return type:

int

Returns: The number of input streams. Negative numbers indicate infeasible configurations.

serialize(process)[source]

Serialize this object’s state.

Objects cannot be serialized directly, instead a Factory must be instructed to carry out the serialization process.

Parameters:

process (SerializationProcess) – The current stage of the serialization process. This object is generated by the Factory and provides an interface to serialization methods supporting multiple backends.

Return type:

None

property num_receive_focus_points: int[source]

Number of required receive focus points.

If this is \(1\), the beamformer is considered to be a single focus point beamformer and receive_focus will return a single focus point. Otherwise, the beamformer is considered a multi focus point beamformer and receive_focus will return a Sequence of focus points.

Returns: Number of focus points.

property num_transmit_focus_points: int[source]

Number of required transmit focus points.

If this is \(1\), the beamformer is considered to be a single focus point beamformer and transmit_focus will return a single focus point. Otherwise, the beamformer is considered a multi focus point beamformer and transmit_focus will return a Sequence of focus points.