Skip to content

Commit

Permalink
Extract array factor methods
Browse files Browse the repository at this point in the history
  • Loading branch information
christianheina committed Mar 12, 2024
1 parent 61bbb54 commit d8a8b82
Show file tree
Hide file tree
Showing 3 changed files with 233 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright 2024 Christian Heina
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.christianheina.communication.jantenna.phasedarray;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.math3.complex.Complex;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;

import com.christianheina.communication.jantenna.commons.Constants;
import com.christianheina.communication.jantenna.commons.ElectricField;
import com.christianheina.communication.jantenna.commons.Field;
import com.christianheina.communication.jantenna.commons.FieldType;
import com.christianheina.communication.jantenna.commons.ThetaPhi;
import com.christianheina.communication.jantenna.commons.WeightableElement;
import com.christianheina.communication.jantenna.phasedarray.config.PhasedArrayAntennaCalculationConfig;
import com.christianheina.communication.jantenna.phasedarray.exceptions.PhasedArrayAntennaException;

/**
* Array factor functionality.
*
* @author Christian Heina ([email protected])
*/
public class ArrayFactor {

private ArrayFactor() {
/* Hidden Constructor */ }

/**
* Create array factor
*
* @param frequency
* wavelength
* @param antennaArray
* antenna array to use when creating array factor
* @param angles
* angles used in the created array factor
*
* @return a field containing the array factor
*/
public static Field newArrayFactorAsync(double frequency, AntennaArray antennaArray, List<ThetaPhi> angles) {
ExecutorService executorService = Executors
.newFixedThreadPool(PhasedArrayAntennaCalculationConfig.getConfig().getNumberOfThreads());
return newArrayFactorAsync(executorService, frequency, antennaArray, angles);
}

/**
* Create array factor
*
* @param executorService
* the ExecutorService used for asynchronous array factor calculations
* @param frequency
* wavelength
* @param antennaArray
* antenna array to use when creating array factor
* @param angles
* angles used in the created array factor
*
* @return a field containing the array factor
*/
public static Field newArrayFactorAsync(ExecutorService executorService, double frequency,
AntennaArray antennaArray, List<ThetaPhi> angles) {
double lambda = Constants.VACCUM_SPEED_OF_LIGHT / frequency;
@SuppressWarnings("unchecked")
CompletableFuture<Complex>[] futureArray = new CompletableFuture[angles.size()];
for (int i = 0; i < angles.size(); i++) {
futureArray[i] = calculateArrayFactorAsync(executorService, lambda, antennaArray, angles.get(i));
}
executorService.shutdown();
try {
CompletableFuture.allOf(futureArray).get();
} catch (InterruptedException | ExecutionException e) {
throw new PhasedArrayAntennaException(
"Encountered unexpected exception while asynchronously calculating array factor", e);
}
List<Complex> fieldDataList = new ArrayList<>(angles.size());
for (int i = 0; i < futureArray.length; i++) {
try {
fieldDataList.add(futureArray[i].get());
} catch (InterruptedException | ExecutionException e) {
throw new PhasedArrayAntennaException(
"Encountered unexpected exception while asynchronously calculating array factor", e);
}
}

return Field.newBuilder().setThetaPhiList(angles).addElectricField(ElectricField.RELATIVE_GAIN, fieldDataList)
.setFreqency(frequency).setFieldType(FieldType.FARFIELD).build();
}

private static CompletableFuture<Complex> calculateArrayFactorAsync(ExecutorService executorService, double lambda,
AntennaArray antennaArray, ThetaPhi angle) {
CompletableFuture<Complex> future = new CompletableFuture<>();

executorService.execute(() -> {
Complex sum = Complex.ZERO;
Vector3D k = PhasedArrayUtil.calculateWaveVector(lambda, angle);
for (WeightableElement antenna : antennaArray.getAntennaArray()) {
Complex vk = PhasedArrayUtil.calculateSteeringVector(k, antenna.getElementLocation());
Complex c = antenna.getElementWeight().multiply(vk);
sum = sum.add(c);
}
future.complete(sum);
});
future.exceptionally(e -> {
throw new PhasedArrayAntennaException(
"Encountered unexpected exception while asynchronously calculating array factor", e);
});
return future;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,11 @@

package com.christianheina.communication.jantenna.phasedarray;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.math3.complex.Complex;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;

import com.christianheina.communication.jantenna.commons.Constants;
import com.christianheina.communication.jantenna.commons.ElectricField;
import com.christianheina.communication.jantenna.commons.Field;
import com.christianheina.communication.jantenna.commons.FieldType;
import com.christianheina.communication.jantenna.commons.ThetaPhi;
import com.christianheina.communication.jantenna.commons.WeightableElement;
import com.christianheina.communication.jantenna.phasedarray.config.PhasedArrayAntennaCalculationConfig;
import com.christianheina.communication.jantenna.phasedarray.exceptions.PhasedArrayAntennaException;

/**
* Phased array functionality.
Expand All @@ -56,11 +43,15 @@ private PhasedArrayAntenna() {
* angles used in the created array factor
*
* @return a field containing the array factor
*
* @deprecated As of 2024-03-13 this is replaced by
* {@link com.christianheina.communication.jantenna.phasedarray.ArrayFactor#newArrayFactorAsync(double, AntennaArray, List)
* newArrayFactorAsync(double frequency, AntennaArray antennaArray, List angles)}. This method will be
* supported until 2024-06-13 and removed 2024-09-13.
*/
@Deprecated
public static Field newArrayFactorAsync(double frequency, AntennaArray antennaArray, List<ThetaPhi> angles) {
ExecutorService executorService = Executors
.newFixedThreadPool(PhasedArrayAntennaCalculationConfig.getConfig().getNumberOfThreads());
return newArrayFactorAsync(executorService, frequency, antennaArray, angles);
return ArrayFactor.newArrayFactorAsync(frequency, antennaArray, angles);
}

/**
Expand All @@ -76,55 +67,16 @@ public static Field newArrayFactorAsync(double frequency, AntennaArray antennaAr
* angles used in the created array factor
*
* @return a field containing the array factor
*
* @deprecated As of 2024-03-13 this is replaced by
* {@link com.christianheina.communication.jantenna.phasedarray.ArrayFactor#newArrayFactorAsync(ExecutorService, double, AntennaArray, List)
* newArrayFactorAsync(ExecutorService executorService, double frequency, AntennaArray antennaArray,
* List angles)}. This method will be supported until 2024-06-13 and removed 2024-09-13.
*/
@Deprecated
public static Field newArrayFactorAsync(ExecutorService executorService, double frequency,
AntennaArray antennaArray, List<ThetaPhi> angles) {
double lambda = Constants.VACCUM_SPEED_OF_LIGHT / frequency;
@SuppressWarnings("unchecked")
CompletableFuture<Complex>[] futureArray = new CompletableFuture[angles.size()];
for (int i = 0; i < angles.size(); i++) {
futureArray[i] = calculateArrayFactorAsync(executorService, lambda, antennaArray, angles.get(i));
}
executorService.shutdown();
try {
CompletableFuture.allOf(futureArray).get();
} catch (InterruptedException | ExecutionException e) {
throw new PhasedArrayAntennaException(
"Encountered unexpected exception while asynchronously calculating array factor", e);
}
List<Complex> fieldDataList = new ArrayList<>(angles.size());
for (int i = 0; i < futureArray.length; i++) {
try {
fieldDataList.add(futureArray[i].get());
} catch (InterruptedException | ExecutionException e) {
throw new PhasedArrayAntennaException(
"Encountered unexpected exception while asynchronously calculating array factor", e);
}
}

return Field.newBuilder().setThetaPhiList(angles).addElectricField(ElectricField.RELATIVE_GAIN, fieldDataList)
.setFreqency(frequency).setFieldType(FieldType.FARFIELD).build();
}

private static CompletableFuture<Complex> calculateArrayFactorAsync(ExecutorService executorService, double lambda,
AntennaArray antennaArray, ThetaPhi angle) {
CompletableFuture<Complex> future = new CompletableFuture<>();

executorService.execute(() -> {
Complex sum = Complex.ZERO;
Vector3D k = PhasedArrayUtil.calculateWaveVector(lambda, angle);
for (WeightableElement antenna : antennaArray.getAntennaArray()) {
Complex vk = PhasedArrayUtil.calculateSteeringVector(k, antenna.getElementLocation());
Complex c = antenna.getElementWeight().multiply(vk);
sum = sum.add(c);
}
future.complete(sum);
});
future.exceptionally(e -> {
throw new PhasedArrayAntennaException(
"Encountered unexpected exception while asynchronously calculating array factor", e);
});
return future;
return ArrayFactor.newArrayFactorAsync(executorService, frequency, antennaArray, angles);
}

/**
Expand Down Expand Up @@ -158,7 +110,7 @@ public static Field newPhasedArray(Field averageEmbeddedAntennaField, Field arra
*/
public static Field newPhasedArrayAsync(Field averageEmbeddedAntennaField, double frequency,
AntennaArray antennaArray, List<ThetaPhi> angles) {
Field arrayFactor = newArrayFactorAsync(frequency, antennaArray, angles);
Field arrayFactor = ArrayFactor.newArrayFactorAsync(frequency, antennaArray, angles);
return newPhasedArray(averageEmbeddedAntennaField, arrayFactor);
}

Expand All @@ -180,7 +132,7 @@ public static Field newPhasedArrayAsync(Field averageEmbeddedAntennaField, doubl
*/
public static Field newPhasedArrayAsync(Field averageEmbeddedAntennaField, ExecutorService executorService,
double frequency, AntennaArray antennaArray, List<ThetaPhi> angles) {
Field arrayFactor = newArrayFactorAsync(executorService, frequency, antennaArray, angles);
Field arrayFactor = ArrayFactor.newArrayFactorAsync(executorService, frequency, antennaArray, angles);
return newPhasedArray(averageEmbeddedAntennaField, arrayFactor);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.christianheina.communication.jantenna.phasedarray;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;

import org.apache.commons.math3.complex.Complex;
import org.testng.Assert;
import org.testng.annotations.Test;

import com.christianheina.communication.jantenna.commons.Constants;
import com.christianheina.communication.jantenna.commons.Field;
import com.christianheina.communication.jantenna.commons.ThetaPhi;
import com.christianheina.communication.jantenna.commons.Util;
import com.christianheina.communication.jantenna.phasedarray.weighting.WeightAlgorithm;

/**
* Unit test for {@link ArrayFactor}.
*
* @author Christian Heina ([email protected])
*/
@SuppressWarnings("javadoc")
public class ArrayFactorTest {

@Test
public void newArrayFactorAsyncSuppliedExecutorServiceTest() {
double freq = 28 * Math.pow(10, 9);
List<ThetaPhi> angleList = ThetaPhi.equallySpacedSphere(1);
ThetaPhi steeringAngle = ThetaPhi.fromDegrees(90, 0);
AntennaArray antennaArray = AntennaArray.fromEquallySpacedArray(1, 24, 16, 0.5, freq,
WeightAlgorithm.newConjugateWeightAlgorithmFromLambda(Util.calculateLambda(freq), steeringAngle));
Field field = ArrayFactor.newArrayFactorAsync(Executors.newSingleThreadExecutor(), freq, antennaArray,
angleList);
Assert.assertEquals(field.getThetaPhiList().size(), angleList.size());
field.getAvailableElectricFields().forEach(electricFieldName -> {
List<Complex> electricFieldList = field.getElectricField(electricFieldName);
double max = Double.MIN_VALUE;
List<ThetaPhi> maxAngleList = new ArrayList<>();
for (int i = 0; i < electricFieldList.size(); i++) {
if (electricFieldList.get(i).abs() >= max) {
max = electricFieldList.get(i).abs();
maxAngleList.add(angleList.get(i));
}
}
boolean hasAngleInList = false;
for (ThetaPhi thetaPhi : maxAngleList) {
if (thetaPhi.getTheta() == steeringAngle.getTheta() && thetaPhi.getPhi() == steeringAngle.getPhi()) {
hasAngleInList = true;
}
}
Assert.assertTrue(hasAngleInList);
});

}

@Test
public void newArrayFactorAsyncTest() {
double freq = 28 * Math.pow(10, 9);
double lambda = Constants.VACCUM_SPEED_OF_LIGHT / freq;
List<ThetaPhi> angleList = ThetaPhi.equallySpacedSphere(1);
ThetaPhi steeringAngle = ThetaPhi.fromDegrees(90, 0);
AntennaArray antennaArray = AntennaArray.fromEquallySpacedArray(1, 24, 16, lambda / 2, freq,
WeightAlgorithm.newConjugateWeightAlgorithmFromLambda(lambda, steeringAngle));
Field field = ArrayFactor.newArrayFactorAsync(freq, antennaArray, angleList);
Assert.assertEquals(field.getThetaPhiList().size(), angleList.size());
field.getAvailableElectricFields().forEach(electricFieldName -> {
List<Complex> electricFieldList = field.getElectricField(electricFieldName);
double max = Double.MIN_VALUE;
List<ThetaPhi> maxAngleList = new ArrayList<>();
for (int i = 0; i < electricFieldList.size(); i++) {
if (electricFieldList.get(i).abs() >= max) {
max = electricFieldList.get(i).abs();
maxAngleList.add(angleList.get(i));
}
}
boolean hasAngleInList = false;
for (ThetaPhi thetaPhi : maxAngleList) {
if (thetaPhi.getTheta() == steeringAngle.getTheta() && thetaPhi.getPhi() == steeringAngle.getPhi()) {
hasAngleInList = true;
}
}
Assert.assertTrue(hasAngleInList);
});

}

}

0 comments on commit d8a8b82

Please sign in to comment.