├── .gitignore ├── README.md ├── misc_functions ├── associatedLegendre.m ├── associatedLegendreDerivative.m ├── nfDataGenerator.m ├── rearrangeTables.m ├── rotateCylindricalNFData.m ├── rotateSphericalNFData.m ├── sphericalVectorWaveFunction.m └── sphericalVectorWaveFunctionFarField.m ├── nf2ff_cylindrical.m ├── nf2ff_planar.m ├── nf2ff_planar_noisy.m ├── nf2ff_spherical.m ├── plot_functions ├── ErrorAnalysis.m ├── HPBWError.m ├── SLLError.m ├── TransformationResult.m ├── plotCylindricalNFData.m ├── plotDiffPhiCut.m ├── plotDiffPhiCutCylindrical.m ├── plotDiffThetaCutCylindrical.m ├── plotFFPhiCut.m ├── plotFFThetaCut.m ├── plotNFPhiCut.m ├── plotNFPhiCutCylindrical.m ├── plotNFThetaCutCylindrical.m ├── plotPlanarNFData.m └── sphere3d.m └── transformation_functions ├── nf2ff_cylindrical_fft.m ├── nf2ff_cylindrical_manual.m ├── nf2ff_planar_fft.m ├── nf2ff_planar_manual.m └── nf2ff_spherical_manual.m /.gitignore: -------------------------------------------------------------------------------- 1 | *.asv 2 | PlanarScan_HornAntenna/ 3 | PlanarScan_Waveguide/ 4 | SphericalScan_HornAntenna/ 5 | measurements_backup/ 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nf2ff_transformation 2 | 3 | This is a set of Matlab scripts and functions to transform near-field 4 | antenna meusurements into far-field. 5 | 6 | ## Transformation Scripts 7 | For each scanning setup (planar,cylindrical,spherical) there is a main script that handles the transformation of the near-field measurements. 8 | To run one of them, adjust the path to the measurement data files inside the first section of the script. 9 | Then select whether you want to use the FFT implementation or the so-called matrix method (by-hand-calculation). 10 | The former being the faster of course. 11 | Afterwards, several different plots can be created. 12 | Either pattern cuts at different angles, the accumulated pattern error, the error in the half-power beamwidth or the error in the first side-lobe level. 13 | In case of the script for the [probe position noise analysis](nf2ff_planar_noisy.m) the above mentioned errors are plotted over the standard devitiation of the positioning noise. 14 | 15 | 16 | ## Additional Functions 17 | For visiualization several additional functions have been implemented. 18 | Using [plotPlanarNFData](plotPlanarNFData.m) and [plotCylindricalNFData](plotCylindricalNFData.m) the data that is read from the measurement files can be plotted. 19 | 20 | Using the [TransformationResults](TransformationResults.m) function, the results of a specific scan can be visualized in 3D either in cartesian or spherical coordinates. 21 | Additionally the difference between the two pattern is plotted. 22 | -------------------------------------------------------------------------------- /misc_functions/associatedLegendre.m: -------------------------------------------------------------------------------- 1 | function [P] = associatedLegendre(n,x) 2 | %ASSOCIATEDLEGENDRE 3 | % Modified associated legendre function to duplicate negative m values 4 | % 5 | % Input Arguments: 6 | % 7 | % n Degree 8 | % x Argument 9 | % 10 | % Output Arguments 11 | % 12 | % P Polynomial Value 13 | 14 | p = legendre(n,x); 15 | P = [flipud(p(2:end,:));p]' ; 16 | 17 | end 18 | 19 | -------------------------------------------------------------------------------- /misc_functions/associatedLegendreDerivative.m: -------------------------------------------------------------------------------- 1 | function [dP] = associatedLegendreDerivative(n,x) 2 | %ASSOCIATEDLEGENDREDERIVATIVE 3 | % First derivative of associated legendre function using recursion formula 4 | % and boundary conditions 5 | % 6 | 7 | m = -n:n; 8 | M = repmat(m,length(x),1); 9 | X = repmat(x,1,length(m)); 10 | Pnm = associatedLegendre(n,x); 11 | 12 | DP1 = Pnm; 13 | 14 | % Handle m=0 15 | i = find(m==0); 16 | DP2 = [Pnm(:,i)./sqrt(1-x.^2) Pnm(:,i+1:end)]; 17 | DP2 = [fliplr(DP2(:,2:end)) DP2]; 18 | 19 | dP = 1./(1-X.^2) .* (n*x.*DP1 -(n+M).*(n-M+1).*sqrt(1-X.^2).*DP2); 20 | 21 | % Handle boundaries at x = +/-1 22 | i1 = find(abs(x+1)<0.0000001); 23 | i2 = find(abs(x-1)<0.0000001); 24 | 25 | k0 = find(abs(m)==0); 26 | k1 = find(abs(m)==1); 27 | k2 = find(abs(m)==2); 28 | k3 = find(abs(m)>=3); 29 | 30 | dP(i1,k0) = n*(n+1)/2; 31 | dP(i1,k1) = Inf; 32 | dP(i1,k2) = -(n-1)*n*(n+1)*(n+2)/4; 33 | dP(i1,k3) = 0; 34 | 35 | dP(i2,k0) = (-1)^(n+1)*n*(n+1)/2; 36 | dP(i2,k1) = (-1)^n*Inf; 37 | dP(i2,k2) = (-1)^n*(n-1)*n*(n+1)*(n+2)/4; 38 | dP(i2,k3) = 0; 39 | 40 | end 41 | 42 | -------------------------------------------------------------------------------- /misc_functions/nfDataGenerator.m: -------------------------------------------------------------------------------- 1 | % 2 | % Creates a Sine-Shaped E field to simulate a waveguide opening 3 | % 4 | close all 5 | % Get the X values for the setup 6 | xValues = data_nf{1}.x; 7 | 8 | % Get Spatial Frequency 9 | k = 2*pi/(2*(max(xValues)-min(xValues))); 10 | 11 | % E-Field 12 | E_norm = sin(k*xValues + pi/2 ); 13 | 14 | data_nf{1}.E = [zeros(length(xValues),1), E_norm, zeros(length(xValues),1)]; 15 | 16 | data_nf{1}.Eabs = vecnorm(data_nf{1}.E,2,2); -------------------------------------------------------------------------------- /misc_functions/rearrangeTables.m: -------------------------------------------------------------------------------- 1 | function [data_nf] = rearrangeTables(data_nf) 2 | 3 | Ex = data_nf.ExReal+ 1j*data_nf.ExImg; 4 | Ey = data_nf.EyReal+ 1j*data_nf.EyImg; 5 | Ez = data_nf.EzReal+ 1j*data_nf.EzImg; 6 | Eabs = data_nf.EabsReal + 1j*data_nf.EabsImg; 7 | %data_nf = table(data_nf.X,data_nf.Y,data_nf.Z,data_nf.R,data_nf.Theta,data_nf.Phi,[Ex,Ey,Ez],Eabs); 8 | %data_nf.Properties.VariableNames = {'x' 'y' 'z' 'r' 'theta' 'phi' 'E' 'Eabs'}; 9 | data_nf = table(data_nf.X,data_nf.Y,data_nf.Z,[Ex,Ey,Ez],Eabs); 10 | data_nf.Properties.VariableNames = {'x' 'y' 'z' 'E' 'Eabs'}; 11 | 12 | data_nf.x = data_nf.x/1000; 13 | data_nf.y = data_nf.y/1000; 14 | data_nf.z = data_nf.z/1000; 15 | 16 | 17 | end 18 | 19 | -------------------------------------------------------------------------------- /misc_functions/rotateCylindricalNFData.m: -------------------------------------------------------------------------------- 1 | function [data_nf_rotated] = rotateCylindricalNFData(data_nf,cylinder_axis) 2 | 3 | % Create Results Table 4 | p = length(data_nf.E); 5 | data_nf_rotated = table(zeros(p,1),zeros(p,1),zeros(p,1),zeros(p,1),zeros(p,1),zeros(p,3),zeros(p,1)); 6 | data_nf_rotated.Properties.VariableNames = {'x','y','z','r','phi','E','Eabs'}; 7 | 8 | data_nf_rotated.x = data_nf.x; 9 | data_nf_rotated.y = data_nf.y; 10 | data_nf_rotated.z = data_nf.z; 11 | 12 | switch cylinder_axis 13 | case 'x' 14 | r = sqrt(data_nf.y.^2 + data_nf.z.^2); 15 | phi = atan2(data_nf.z,data_nf.y); 16 | for i =1:length(data_nf.E) 17 | R = [1 0 0;... 18 | 0 cos(phi(i)) sin(phi(i));... 19 | 0 -sin(phi(i)) cos(phi(i))]; 20 | data_nf_rotated.E(i,:) = (R*data_nf.E(i,:)')'; 21 | data_nf_rotated.r(i) = r(i); 22 | data_nf_rotated.phi(i) = phi(i); 23 | data_nf_rotated.Eabs(i) = data_nf.Eabs(i); 24 | end 25 | 26 | case 'y' 27 | r = sqrt(data_nf.x.^2 + data_nf.z.^2); 28 | phi = round(atan2(data_nf.x,data_nf.z),6); 29 | for i =1:length(data_nf.E) 30 | R = [cos(phi(i)) 0 sin(phi(i)) ;... 31 | 0 1 0;... 32 | -sin(phi(i)) 0 cos(phi(i))]; 33 | data_nf_rotated.E(i,:) = (R*data_nf.E(i,:)')'; 34 | data_nf_rotated.r(i) = r(i); 35 | data_nf_rotated.phi(i) = phi(i); 36 | data_nf_rotated.Eabs(i) = data_nf.Eabs(i); 37 | end 38 | 39 | case 'z' 40 | r = sqrt(data_nf.x.^2 + data_nf.y.^2); 41 | phi = round(atan2(data_nf.y,data_nf.x),6); 42 | idx = find(phi < 0); 43 | phi(idx) = 2*pi + phi(idx); 44 | for i =1:length(data_nf.E) 45 | R = [cos(phi(i)) sin(phi(i)) 0;... 46 | -sin(phi(i)) cos(phi(i)) 0;... 47 | 0 0 1]; 48 | data_nf_rotated.E(i,:) = (R*data_nf.E(i,:)')'; 49 | data_nf_rotated.r(i) = r(i); 50 | data_nf_rotated.phi(i) = phi(i); 51 | data_nf_rotated.Eabs(i) = data_nf.Eabs(i); 52 | end 53 | end 54 | 55 | -------------------------------------------------------------------------------- /misc_functions/rotateSphericalNFData.m: -------------------------------------------------------------------------------- 1 | function [data_nf_rotated] = rotateSphericalNFData(data_nf) 2 | 3 | % Create Results Table 4 | p = length(data_nf.E); 5 | data_nf_rotated = table(zeros(p,1),zeros(p,1),zeros(p,1),zeros(p,1),zeros(p,1),zeros(p,1),zeros(p,3),zeros(p,1)); 6 | data_nf_rotated.Properties.VariableNames = {'x','y','z','r','theta','phi','E','Eabs'}; 7 | 8 | data_nf_rotated.x = data_nf.x; 9 | data_nf_rotated.y = data_nf.y; 10 | data_nf_rotated.z = data_nf.z; 11 | 12 | r = sqrt(data_nf.x.^2 + data_nf.y.^2 + data_nf.z.^2); 13 | theta = acos(data_nf.z./r); 14 | phi = atan2(data_nf.y,data_nf.x); 15 | for i =1:length(data_nf.E) 16 | R = [sin(theta(i))*cos(phi(i)) sin(theta(i))*sin(phi(i)) cos(theta(i));... 17 | cos(theta(i))*cos(phi(i)) cos(theta(i))*sin(phi(i)) -sin(theta(i));... 18 | -sin(phi(i)) cos(phi(i)) 0]; 19 | 20 | %E(1)-> Radial,E(2)->Theta,E(3)->Phi 21 | data_nf_rotated.E(i,:) = (R*data_nf.E(i,:)')'; 22 | data_nf_rotated.r(i) = r(i); 23 | data_nf_rotated.theta(i) = theta(i); 24 | if phi(i) <0 25 | data_nf_rotated.phi(i) = pi - phi(i); 26 | else 27 | data_nf_rotated.phi(i) = phi(i); 28 | end 29 | data_nf_rotated.Eabs(i) = data_nf.Eabs(i); 30 | end 31 | -------------------------------------------------------------------------------- /misc_functions/sphericalVectorWaveFunction.m: -------------------------------------------------------------------------------- 1 | function [fr,ftheta,fphi, Y] = sphericalVectorWaveFunction(s,m,n,A,theta,phi,k) 2 | %SPHERICALVECTORWAVEFUNCTION 3 | % 4 | % Input Arguments: 5 | % 6 | % s Parity 7 | % m,n Iterators over 8 | % A Radius 9 | % theta Theta angle 10 | % phi Phi angle 11 | % k Wavenumber at given frequency 12 | % 13 | % Output Arguments: 14 | % 15 | % fr Radial Component of Spherical wave function 16 | % 17 | % ftheta Theta Component of Spherical wave function 18 | % 19 | % fphi Phi Component of Spherical wave function 20 | % 21 | % Y Factor that shows up when solving for the spherical wave 22 | % expansion coefficients 23 | % 24 | 25 | % Helper Functions 26 | z =@(n,r) sqrt(pi/(2*r)).* besselh(n+0.5,r); 27 | dz = @(n,r) z(n-1,r) - (n+1)/r * z(n,r); % CHECK IF THIS IS CORRECT!! 28 | delta = @(s,sigma) 0.5*(1+(-1)^(s+sigma)); 29 | 30 | 31 | %%%% Note %%% 32 | % R,T and P provide values for each |m| to speed up the process 33 | % This means the f output will be a matrix with m columns 34 | 35 | % Radial Function 36 | R = @(s,n,r) delta(s,1).*z(n,k*r) + delta(s,2).*1/(k*r).* dz(n,k*r); 37 | % Theta Function 38 | T = @(s,m,n,theta) delta(s,1)*1j*m./sin(theta) .* associatedLegendre(n,cos(theta))... 39 | + delta(s,2) .* associatedLegendreDerivative(n,cos(theta)); % Add derivative of Associated Legendre Function; 40 | 41 | % Phi Function 42 | P = @(m,phi) exp(1j*m.*repmat(phi,1,size(m,2))); 43 | 44 | 45 | 46 | % Radial Component 47 | fr = delta(s,2)*n*(n+1)/(k*A) *z(n,k*A) * associatedLegendre(n,cos(theta)).*P(m,theta); 48 | % Theta Component 49 | ftheta = repmat(R(s,n,A),1,size(m,2)).*T(s,m,n,theta).*P(m,phi); 50 | % Phi Component 51 | fphi = (-1)^s.*repmat(R(s,n,A),1,size(m,2)).*T(s+1,m,n,theta).*P(m,theta); 52 | 53 | 54 | 55 | % Additional Factor that shows up when solving for the spherical wave 56 | % expansion coefficients. 57 | Y = 4*pi/(2*n + 1) *factorial(n+abs(m(1,:)))./factorial(n-abs(m(1,:))) *n*(n+1)*R(s,n,A).^2; 58 | 59 | end 60 | 61 | -------------------------------------------------------------------------------- /misc_functions/sphericalVectorWaveFunctionFarField.m: -------------------------------------------------------------------------------- 1 | function [xtheta,xphi] = sphericalVectorWaveFunctionFarField(s,m,n,A,theta,phi,k) 2 | %SPHERICALVECTORWAVEFUNCTION 3 | % Far-Field approximation of the spherical vector wave function 4 | % 5 | % Input Arguments: 6 | % 7 | % s Parity 8 | % m,n Iterators over 9 | % A Radius 10 | % theta Theta angle 11 | % phi Phi angle 12 | % k Wavenumber at given frequency 13 | % 14 | % Output Arguments: 15 | % 16 | % xtheta Theta Component of Spherical wave function 17 | % 18 | % xphi Phi Component of Spherical wave function 19 | % 20 | 21 | 22 | % Helper Functions 23 | delta = @(s,sigma) 0.5*(1+(-1)^(s+sigma)); 24 | 25 | 26 | %%%% Note %%% 27 | % T and P provide values for each |m| to speed up the process 28 | % This means the f output will be a matrix with m columns 29 | 30 | % Theta Function 31 | T = @(s,m,n,theta) delta(s,1)*1j*m./sin(theta) .* associatedLegendre(n,cos(theta))... 32 | + delta(s,2).*associatedLegendreDerivative(n,cos(theta)); 33 | % Phi Function 34 | P = @(m,phi) exp(1j*m.*repmat(phi,1,size(m,2))); 35 | 36 | 37 | 38 | 39 | C = 1/(k*A) * (-1j)^(n+delta(s,1)) * exp(1j*k*A) *P(m,phi); 40 | % Theta Component 41 | xtheta = C .* T(s,m,n,theta); 42 | % Phi Component 43 | xphi = C .* (-1)^s .* T(s+1,m,n,theta); 44 | 45 | 46 | 47 | 48 | end 49 | 50 | -------------------------------------------------------------------------------- /nf2ff_cylindrical.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbartle/nf2ff_transformation/01641d29cb3b662686bef766f21fd8c5309eb007/nf2ff_cylindrical.m -------------------------------------------------------------------------------- /nf2ff_planar.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbartle/nf2ff_transformation/01641d29cb3b662686bef766f21fd8c5309eb007/nf2ff_planar.m -------------------------------------------------------------------------------- /nf2ff_planar_noisy.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbartle/nf2ff_transformation/01641d29cb3b662686bef766f21fd8c5309eb007/nf2ff_planar_noisy.m -------------------------------------------------------------------------------- /nf2ff_spherical.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbartle/nf2ff_transformation/01641d29cb3b662686bef766f21fd8c5309eb007/nf2ff_spherical.m -------------------------------------------------------------------------------- /plot_functions/ErrorAnalysis.m: -------------------------------------------------------------------------------- 1 | function [E] = ErrorAnalysis(data_ff,data_nf2ff,scanner_case) 2 | %ERRORANALYSIS Create MSE for pattern 3 | % 4 | 5 | switch scanner_case 6 | case 'planar' 7 | 8 | % Transformed Near-field 9 | n_theta_nf2ff = length(unique(data_nf2ff.theta)); 10 | n_phi_nf2ff = length(unique(data_nf2ff.phi)); 11 | 12 | X_nf2ff = reshape(data_nf2ff.theta,n_theta_nf2ff,n_phi_nf2ff); 13 | Y_nf2ff = reshape(data_nf2ff.phi,n_theta_nf2ff,n_phi_nf2ff); 14 | Z_nf2ff = reshape(data_nf2ff.Eabs,n_theta_nf2ff,n_phi_nf2ff); 15 | 16 | 17 | X_nf2ff = [X_nf2ff(:,end/2+1:end);fliplr(-X_nf2ff(:,2:end/2+1)) ]'; 18 | Y_nf2ff = [Y_nf2ff(:,end/2+1:end); pi+fliplr(Y_nf2ff(:,2:end/2+1))]'; 19 | Z_nf2ff = [Z_nf2ff(:,end/2+1:end); fliplr(Z_nf2ff(:,2:end/2+1))]'; 20 | 21 | maxTheta = size(Z_nf2ff,1); 22 | maxPhi = size(Z_nf2ff,2); 23 | 24 | % Far Field 25 | i =find(data_ff.theta~=pi); % Clean up the duplicate samples 26 | n_theta_ff = length(unique(data_ff.theta(i))); 27 | n_phi_ff = length(unique(data_ff.phi(i))); 28 | 29 | X_ff = reshape(data_ff.theta(i),n_theta_ff,n_phi_ff); 30 | Y_ff = reshape(data_ff.phi(i),n_theta_ff,n_phi_ff); 31 | Z_ff = reshape(data_ff.Eabs(i),n_theta_ff,n_phi_ff); 32 | 33 | X_ff = [X_ff(1:end/2,:); X_ff(end/2+1:end,:)]; 34 | Y_ff = [Y_ff(1:end/2,:); Y_ff(end/2+1:end,:)]; 35 | Z_ff = [Z_ff(1:end/2,:); Z_ff(end/2+1:end,:)]; 36 | 37 | 38 | X_ff = X_ff(1:maxTheta,1:maxPhi); 39 | Y_ff = Y_ff(1:maxTheta,1:maxPhi); 40 | Z_ff = Z_ff(1:maxTheta,1:maxPhi); 41 | 42 | case 'cylindrical' 43 | 44 | % Transformed Near-field 45 | n_theta_nf2ff = length(unique(data_nf2ff.theta)); 46 | n_phi_nf2ff = length(unique(data_nf2ff.phi)); 47 | 48 | X_nf2ff = reshape(data_nf2ff.theta,n_phi_nf2ff,n_theta_nf2ff)'; 49 | Y_nf2ff = reshape(data_nf2ff.phi,n_phi_nf2ff,n_theta_nf2ff)'; 50 | Z_nf2ff = reshape(data_nf2ff.Eabs,n_phi_nf2ff,n_theta_nf2ff)'; 51 | 52 | 53 | 54 | % Far Field 55 | i =find(data_ff.theta~=pi); % Clean up the duplicate samples 56 | n_theta_ff = length(unique(data_ff.theta(i))); 57 | n_phi_ff = length(unique(data_ff.phi(i))); 58 | 59 | X_ff = reshape(data_ff.theta(i),n_theta_ff,n_phi_ff); 60 | Y_ff = reshape(data_ff.phi(i),n_theta_ff,n_phi_ff); 61 | Z_ff = reshape(data_ff.Eabs(i),n_theta_ff,n_phi_ff); 62 | 63 | case 'spherical' 64 | 65 | 66 | 67 | 68 | end 69 | 70 | 71 | %% Normalized MSE 72 | Z_ff_norm = Z_ff/max(max(Z_ff)); 73 | Z_nf2ff_norm = Z_nf2ff/max(max(Z_nf2ff)); 74 | 75 | E = sqrt(sum((Z_nf2ff_norm(:) - Z_ff_norm(:)).^2)/sum(Z_ff_norm(:).^2)); 76 | 77 | 78 | 79 | end 80 | 81 | -------------------------------------------------------------------------------- /plot_functions/HPBWError.m: -------------------------------------------------------------------------------- 1 | function [hpbw_ff,hpbw_nf2ff,hpbw_err] = HPBWError(data_ff,data_nf2ff,scanner_case) 2 | %HPBWERROR Calculate the error in the Half-Power Beamwidth of the antenna 3 | %pattern. 4 | 5 | 6 | %% Phi=0 cutting plane 7 | phi_cut = 0; 8 | 9 | % Far-Field 10 | i = find(data_ff.phi==phi_cut); 11 | m = find(data_ff.phi==phi_cut+pi); 12 | ff_cut_angles = [-fliplr(data_ff.theta(m)') data_ff.theta(i)']; 13 | maxValue1 = (max(data_ff.Eabs(i))); 14 | maxValue2 = (max(data_ff.Eabs(m))); 15 | 16 | if maxValue1>maxValue2 17 | maxValue =maxValue1; 18 | else 19 | maxValue=maxValue2; 20 | end 21 | 22 | ff_cut = [fliplr(data_ff.Eabs(m)')'; data_ff.Eabs(i)]/maxValue; 23 | 24 | 25 | % Near Field 26 | switch scanner_case 27 | case 'planar' 28 | i = find(data_nf2ff.phi==phi_cut); 29 | nf2ff_cut_angles = data_nf2ff.theta(i)'; 30 | maxValue = max(max(data_nf2ff.Eabs(i))); 31 | nf2ff_cut = data_nf2ff.Eabs(i)/maxValue; 32 | 33 | case 'cylindrical' 34 | i = find(abs(data_nf2ff.phi-phi_cut)<0.000001); 35 | m = find(abs(data_nf2ff.phi-(phi_cut+pi))<0.000001); 36 | nf2ff_cut_angles = [-fliplr(data_nf2ff.theta(m)') data_nf2ff.theta(i)']; 37 | maxValue1 = (max(data_nf2ff.Eabs(i))); 38 | maxValue2 = (max(data_nf2ff.Eabs(m))); 39 | 40 | if maxValue1>maxValue2 41 | maxValue =maxValue1; 42 | else 43 | maxValue=maxValue2; 44 | end 45 | nf2ff_cut = [fliplr(data_nf2ff.Eabs(m)')'; data_nf2ff.Eabs(i)]/maxValue; 46 | 47 | case 'spherical' 48 | end 49 | 50 | 51 | % Find FF-HPBW 52 | i = find(ff_cut>0.5); 53 | angle_res = mean(diff(ff_cut_angles)); 54 | hpbw_ff(1) = (ff_cut_angles(i(end)) - ff_cut_angles(i(1)))*180/pi; 55 | hpbw_ff(1) = length(i)*angle_res*180/pi; 56 | 57 | % Find NF-HPBW 58 | i = find(nf2ff_cut>0.5); 59 | angle_res = mean(diff(nf2ff_cut_angles)); 60 | hpbw_nf2ff(1) = (nf2ff_cut_angles(i(end)) - nf2ff_cut_angles(i(1)))*180/pi; 61 | hpbw_nf2ff(1) = length(i)*angle_res*180/pi; 62 | 63 | 64 | %% Phi=90 / Theta = 90 65 | 66 | % Near Field 67 | switch scanner_case 68 | case 'planar' 69 | phi_cut = pi/2; 70 | % Far-Field 71 | i = find(data_ff.phi==phi_cut); 72 | m = find(data_ff.phi==phi_cut+pi); 73 | ff_cut_angles = [-fliplr(data_ff.theta(m)') data_ff.theta(i)']; 74 | maxValue1 = (max(data_ff.Eabs(i))); 75 | maxValue2 = (max(data_ff.Eabs(m))); 76 | 77 | if maxValue1>maxValue2 78 | maxValue =maxValue1; 79 | else 80 | maxValue=maxValue2; 81 | end 82 | 83 | ff_cut = [fliplr(data_ff.Eabs(m)')'; data_ff.Eabs(i)]/maxValue; 84 | 85 | % Near-Field 86 | i = find(data_nf2ff.phi==phi_cut); 87 | nf2ff_cut_angles = data_nf2ff.theta(i)'; 88 | maxValue = max(max(data_nf2ff.Eabs(i))); 89 | nf2ff_cut = data_nf2ff.Eabs(i)/maxValue; 90 | 91 | 92 | 93 | case 'cylindrical' 94 | % Cut in Theta=90 plane since antenna is directed in positive 95 | % x-axis 96 | 97 | % Far-Field 98 | theta_cut=pi/2; 99 | i = find(data_ff.theta==theta_cut); 100 | ff_cut_angles = data_ff.phi(i); 101 | maxValue = max(data_ff.Eabs(i)); 102 | ff_cut = data_ff.Eabs(i)/maxValue; 103 | 104 | 105 | % Near-field 106 | % Find values in cutting plane 107 | i = find(abs(data_nf2ff.theta-theta_cut)<0.000001); 108 | nf2ff_cut_angles = data_nf2ff.phi(i); 109 | maxValue = max(max(data_nf2ff.Eabs(i))); 110 | nf2ff_cut = data_nf2ff.Eabs(i)/maxValue; 111 | 112 | 113 | case 'spherical' 114 | end 115 | 116 | % Find FF-HPBW 117 | i = find(ff_cut>0.5); 118 | angle_res = mean(diff(ff_cut_angles)); 119 | % hpbw_ff(2) = (ff_cut_angles(i(end)) - ff_cut_angles(i(1)))*180/pi; 120 | hpbw_ff(2) = length(i)*angle_res*180/pi; 121 | 122 | % Find NF-HPBW 123 | i = find(nf2ff_cut>0.5); 124 | angle_res = mean(diff(nf2ff_cut_angles)); 125 | % hpbw_nf2ff(2) = (nf2ff_cut_angles(i(end)) - nf2ff_cut_angles(i(1)))*180/pi; 126 | hpbw_nf2ff(2) = length(i)*angle_res*180/pi; 127 | 128 | 129 | %% Find HPBW Error 130 | hpbw_err = hpbw_ff - hpbw_nf2ff; 131 | 132 | % Transpose results 133 | hpbw_ff = hpbw_ff'; 134 | hpbw_nf2ff = hpbw_nf2ff'; 135 | hpbw_err = hpbw_err'; 136 | end 137 | 138 | -------------------------------------------------------------------------------- /plot_functions/SLLError.m: -------------------------------------------------------------------------------- 1 | function [sll_ff,sll_nf2ff,sll_err] = SLLError(data_ff,data_nf2ff,scanner_case) 2 | %SLLERROR Side-Lobe-Level Error analysis 3 | 4 | 5 | switch scanner_case 6 | case 'planar' 7 | phi_cut = pi/2; 8 | % Far-Field 9 | i = find(data_ff.phi==phi_cut); 10 | m = find(data_ff.phi==phi_cut+pi); 11 | ff_cut_angles = [-fliplr(data_ff.theta(m)') data_ff.theta(i)']; 12 | maxValue1 = (max(data_ff.Eabs(i))); 13 | maxValue2 = (max(data_ff.Eabs(m))); 14 | if maxValue1>maxValue2 15 | maxValue =maxValue1; 16 | else 17 | maxValue=maxValue2; 18 | end 19 | ff_cut = [fliplr(data_ff.Eabs(m)')'; data_ff.Eabs(i)]/maxValue; 20 | 21 | 22 | % Near Field 23 | i = find(data_nf2ff.phi==phi_cut); 24 | nf2ff_cut_angles = data_nf2ff.theta(i)'; 25 | maxValue = max(max(data_nf2ff.Eabs(i))); 26 | nf2ff_cut = data_nf2ff.Eabs(i)/maxValue; 27 | 28 | case 'cylindrical' 29 | phi_cut=0; 30 | % Far-Field 31 | i = find(data_ff.phi==phi_cut); 32 | m = find(data_ff.phi==phi_cut+pi); 33 | ff_cut_angles = [-fliplr(data_ff.theta(m)') data_ff.theta(i)']; 34 | maxValue1 = (max(data_ff.Eabs(i))); 35 | maxValue2 = (max(data_ff.Eabs(m))); 36 | 37 | if maxValue1>maxValue2 38 | maxValue =maxValue1; 39 | else 40 | maxValue=maxValue2; 41 | end 42 | ff_cut = [fliplr(data_ff.Eabs(m)')'; data_ff.Eabs(i)]/maxValue; 43 | 44 | % Near-Field 45 | i = find(abs(data_nf2ff.phi-phi_cut)<0.000001); 46 | m = find(abs(data_nf2ff.phi-(phi_cut+pi))<0.000001); 47 | nf2ff_cut_angles = [-fliplr(data_nf2ff.theta(m)') data_nf2ff.theta(i)']; 48 | maxValue1 = (max(data_nf2ff.Eabs(i))); 49 | maxValue2 = (max(data_nf2ff.Eabs(m))); 50 | 51 | if maxValue1>maxValue2 52 | maxValue =maxValue1; 53 | else 54 | maxValue=maxValue2; 55 | end 56 | nf2ff_cut = [fliplr(data_nf2ff.Eabs(m)')'; data_nf2ff.Eabs(i)]/maxValue; 57 | 58 | case 'spherical' 59 | end 60 | 61 | 62 | % Side-Lobe Level Far-Field 63 | [pks_ff,idx_ff] = findpeaks(ff_cut); 64 | [m,i] = max(pks_ff(pks_ff<0.5)); 65 | sll_ff = 20*log10(m); 66 | 67 | if pks_ff(i) == 1 68 | i = i+1; 69 | end 70 | sll_nf2ff = 20*log10(nf2ff_cut(idx_ff(i)-90)); 71 | 72 | sll_err = sll_ff - sll_nf2ff; 73 | 74 | end 75 | 76 | -------------------------------------------------------------------------------- /plot_functions/TransformationResult.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbartle/nf2ff_transformation/01641d29cb3b662686bef766f21fd8c5309eb007/plot_functions/TransformationResult.m -------------------------------------------------------------------------------- /plot_functions/plotCylindricalNFData.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbartle/nf2ff_transformation/01641d29cb3b662686bef766f21fd8c5309eb007/plot_functions/plotCylindricalNFData.m -------------------------------------------------------------------------------- /plot_functions/plotDiffPhiCut.m: -------------------------------------------------------------------------------- 1 | function [] = plotDiffPhiCut(data_nf2ff,data_ff,phi,theta_range) 2 | 3 | 4 | % Normalized Far-Field cut 5 | i = find(data_ff.phi==phi & ismember(data_ff.theta,theta_range)); 6 | m = find(data_ff.phi==phi+pi & ismember(data_ff.theta,theta_range)); 7 | ff_cut_angles = [-fliplr(data_ff.theta(m)') data_ff.theta(i)']; 8 | ff_cut = [fliplr(data_ff.Eabs(m)')'; data_ff.Eabs(i)]/max(data_ff.Eabs); 9 | 10 | 11 | % Normalized NF2FF cut 12 | i = find(data_nf2ff.phi==phi); 13 | 14 | nf2ff_cut_angles = data_nf2ff.theta(i)'; 15 | maxValue = max(max(data_nf2ff.Eabs(i))); 16 | nf2ff_cut = data_nf2ff.Eabs(i)/maxValue; 17 | 18 | plot(nf2ff_cut_angles*180/pi,20*log10(abs(ff_cut - nf2ff_cut))) 19 | hold on 20 | end 21 | 22 | -------------------------------------------------------------------------------- /plot_functions/plotDiffPhiCutCylindrical.m: -------------------------------------------------------------------------------- 1 | function [] = plotDiffPhiCutCylindrical(data_nf2ff,data_ff,phi,theta_range) 2 | 3 | 4 | % Normalized Far-Field cut 5 | i = find(data_ff.phi==phi & ismember(data_ff.theta,theta_range)); 6 | m = find(data_ff.phi==phi+pi & ismember(data_ff.theta,theta_range)); 7 | ff_cut_angles = [-fliplr(data_ff.theta(m)') data_ff.theta(i)']; 8 | maxValue1 = (max(data_ff.Eabs(i))); 9 | maxValue2 = (max(data_ff.Eabs(m))); 10 | 11 | if maxValue1>maxValue2 12 | maxValue =maxValue1; 13 | else 14 | maxValue=maxValue2; 15 | end 16 | 17 | ff_cut = [fliplr(data_ff.Eabs(m)')'; data_ff.Eabs(i)]/maxValue; 18 | 19 | 20 | % Normalized NF2FF cut 21 | i = find(abs(data_nf2ff.phi-phi)<0.000001); 22 | m = find(abs(data_nf2ff.phi-(phi+pi))<0.000001); 23 | nf2ff_cut_angles = [-fliplr(data_nf2ff.theta(m)') data_nf2ff.theta(i)']; 24 | maxValue = max(max(data_nf2ff.Eabs(i))); 25 | nf2ff_cut = [fliplr(data_nf2ff.Eabs(m)')'; data_nf2ff.Eabs(i)]/maxValue; 26 | 27 | 28 | plot(nf2ff_cut_angles*180/pi,20*log10(abs(ff_cut - nf2ff_cut))) 29 | hold on 30 | end 31 | 32 | -------------------------------------------------------------------------------- /plot_functions/plotDiffThetaCutCylindrical.m: -------------------------------------------------------------------------------- 1 | function [] = plotDiffThetaCutCylindrical(data_nf2ff,data_ff,theta) 2 | phi_range = unique(data_nf2ff.phi); 3 | 4 | 5 | % Normalized Far-Field cut 6 | 7 | i = find(data_ff.theta==theta & ismember(round(data_ff.phi*180/pi,0),round(phi_range*180/pi,0))); 8 | ff_cut_angles = data_ff.phi(i); 9 | ff_cut = data_ff.Eabs(i)/max(data_ff.Eabs); 10 | 11 | 12 | % Normalized NF2FF cut 13 | i = find(abs(data_nf2ff.theta-theta)<0.000001); 14 | 15 | nf2ff_cut_angles = data_nf2ff.phi(i); 16 | maxValue = max(max(data_nf2ff.Eabs(i))); 17 | nf2ff_cut = data_nf2ff.Eabs(i)/maxValue; 18 | 19 | 20 | plot(nf2ff_cut_angles*180/pi,20*log10(abs(ff_cut - nf2ff_cut))) 21 | hold on 22 | xlim([0 360]) 23 | end 24 | 25 | -------------------------------------------------------------------------------- /plot_functions/plotFFPhiCut.m: -------------------------------------------------------------------------------- 1 | function [] = plotFFPhiCut(data_ff,phi,normalized,logarithmic) 2 | 3 | 4 | i = find(data_ff.phi==phi); 5 | m = find(data_ff.phi==phi+pi); 6 | ff_cut_angles = [-fliplr(data_ff.theta(m)') data_ff.theta(i)']; 7 | maxValue1 = (max(data_ff.Eabs(i))); 8 | maxValue2 = (max(data_ff.Eabs(m))); 9 | 10 | if maxValue1>maxValue2 11 | maxValue =maxValue1; 12 | else 13 | maxValue=maxValue2; 14 | end 15 | 16 | if normalized == true 17 | ff_cut = [fliplr(data_ff.Eabs(m)')'; data_ff.Eabs(i)]/maxValue; 18 | else 19 | ff_cut = [fliplr(data_ff.Eabs(m)')'; data_ff.Eabs(i)]; 20 | end 21 | 22 | if logarithmic == true 23 | plot(ff_cut_angles*180/pi,20*log10(ff_cut)) 24 | elseif logarithmic == false 25 | plot(ff_cut_angles*180/pi,ff_cut) 26 | end 27 | hold on 28 | 29 | end 30 | 31 | -------------------------------------------------------------------------------- /plot_functions/plotFFThetaCut.m: -------------------------------------------------------------------------------- 1 | function [] = plotFFThetaCut(data_ff,theta,normalized,logarithmic) 2 | 3 | 4 | i = find(data_ff.theta==theta); 5 | ff_cut_angles = data_ff.phi(i); 6 | maxValue = max(data_ff.Eabs(i)); 7 | if normalized == true 8 | ff_cut = data_ff.Eabs(i)/maxValue; 9 | else 10 | ff_cut = data_ff.Eabs(i); 11 | end 12 | 13 | if logarithmic == true 14 | plot(ff_cut_angles*180/pi,20*log10(ff_cut)) 15 | elseif logarithmic == false 16 | plot(ff_cut_angles*180/pi,ff_cut) 17 | end 18 | hold on 19 | 20 | end 21 | 22 | -------------------------------------------------------------------------------- /plot_functions/plotNFPhiCut.m: -------------------------------------------------------------------------------- 1 | function [] = plotNFPhiCut(data_nf2ff,phi_cut,normalized,logarithmic) 2 | 3 | 4 | % Find values in cutting plane 5 | i = find(data_nf2ff.phi==phi_cut); 6 | 7 | % Flip one have of the values to make continous plot 8 | nf2ff_cut_angles = data_nf2ff.theta(i)'; 9 | maxValue = max(max(data_nf2ff.Eabs(i))); 10 | if normalized == true 11 | nf2ff_cut = data_nf2ff.Eabs(i)/maxValue; 12 | else 13 | nf2ff_cut = data_nf2ff.Eabs(i); 14 | end 15 | 16 | if logarithmic == true 17 | plot(nf2ff_cut_angles*180/pi,20*log10(nf2ff_cut)) 18 | elseif logarithmic == false 19 | plot(nf2ff_cut_angles*180/pi,nf2ff_cut) 20 | end 21 | hold on 22 | end 23 | 24 | -------------------------------------------------------------------------------- /plot_functions/plotNFPhiCutCylindrical.m: -------------------------------------------------------------------------------- 1 | function [] = plotNFPhiCutCylindrical(data_nf2ff,phi,normalized,logarithmic) 2 | 3 | % Find values in cutting plane 4 | i = find(abs(data_nf2ff.phi-phi)<0.000001); 5 | m = find(abs(data_nf2ff.phi-(phi+pi))<0.000001); 6 | nf2ff_cut_angles = [-fliplr(data_nf2ff.theta(m)') data_nf2ff.theta(i)']; 7 | maxValue1 = (max(data_nf2ff.Eabs(i))); 8 | maxValue2 = (max(data_nf2ff.Eabs(m))); 9 | 10 | if maxValue1>maxValue2 11 | maxValue =maxValue1; 12 | else 13 | maxValue=maxValue2; 14 | end 15 | 16 | if normalized == true 17 | nf2ff_cut = [fliplr(data_nf2ff.Eabs(m)')'; data_nf2ff.Eabs(i)]/maxValue; 18 | else 19 | nf2ff_cut = [fliplr(data_nf2ff.Eabs(m)')'; data_nf2ff.Eabs(i)]; 20 | end 21 | 22 | if logarithmic == true 23 | plot(nf2ff_cut_angles*180/pi,20*log10(nf2ff_cut)) 24 | elseif logarithmic == false 25 | plot(nf2ff_cut_angles*180/pi,nf2ff_cut) 26 | end 27 | hold on 28 | end 29 | 30 | -------------------------------------------------------------------------------- /plot_functions/plotNFThetaCutCylindrical.m: -------------------------------------------------------------------------------- 1 | function [] = plotNFThetaCutCylindrical(data_nf2ff,theta,normalized,logarithmic) 2 | 3 | % Find values in cutting plane 4 | i = find(abs(data_nf2ff.theta-theta)<0.000001); 5 | nf2ff_cut_angles = data_nf2ff.phi(i); 6 | maxValue = max(max(data_nf2ff.Eabs(i))); 7 | 8 | if normalized == true 9 | nf2ff_cut = data_nf2ff.Eabs(i)/maxValue; 10 | else 11 | nf2ff_cut = data_nf2ff.Eabs(i); 12 | end 13 | 14 | if logarithmic == true 15 | plot(nf2ff_cut_angles*180/pi,20*log10(nf2ff_cut)) 16 | elseif logarithmic == false 17 | plot(nf2ff_cut_angles*180/pi,nf2ff_cut) 18 | end 19 | hold on 20 | end 21 | 22 | -------------------------------------------------------------------------------- /plot_functions/plotPlanarNFData.m: -------------------------------------------------------------------------------- 1 | function [] = plotPlanarNFData(data_nf) 2 | 3 | figure('name','Near-Field Magnitude','numbertitle','off',... 4 | 'units','normalized','outerposition',[0 0 1 1]); 5 | 6 | numelX= sqrt(length(data_nf.x)); 7 | numelY= sqrt(length(data_nf.x)); 8 | 9 | 10 | surf(reshape(data_nf.x,numelX,numelY),... 11 | reshape(data_nf.y,numelX,numelY),... 12 | reshape(data_nf.Eabs,numelX,numelY)) 13 | title(['Scan ' num2str(numelX) 'x' num2str(numelY) 'Magnitude']) 14 | 15 | % figure('name','Near-Field Phase','numbertitle','off',... 16 | % 'units','normalized','outerposition',[0 0 1 1]); 17 | % 18 | % numelX= sqrt(length(data_nf.x)); 19 | % numelY= sqrt(length(data_nf.x)); 20 | % 21 | % 22 | % surf(reshape(data_nf.x,numelX,numelY),... 23 | % reshape(data_nf.y,numelX,numelY),... 24 | % reshape(angle(data_nf.E(:,1))*180/pi,numelX,numelY)) 25 | % title(['Scan ' num2str(numelX) 'x' num2str(numelY) ' Phase X Component']) 26 | % figure('name','Near-Field Phase','numbertitle','off',... 27 | % 'units','normalized','outerposition',[0 0 1 1]); 28 | % 29 | % numelX= sqrt(length(data_nf.x)); 30 | % numelY= sqrt(length(data_nf.x)); 31 | % 32 | % 33 | % surf(reshape(data_nf.x,numelX,numelY),... 34 | % reshape(data_nf.y,numelX,numelY),... 35 | % reshape(angle(data_nf.E(:,2))*180/pi,numelX,numelY)) 36 | % title(['Scan ' num2str(numelX) 'x' num2str(numelY) ' Phase Y Component']) 37 | % figure('name','Near-Field Phase','numbertitle','off',... 38 | % 'units','normalized','outerposition',[0 0 1 1]); 39 | % 40 | % numelX= sqrt(length(data_nf.x)); 41 | % numelY= sqrt(length(data_nf.x)); 42 | % 43 | % 44 | % surf(reshape(data_nf.x,numelX,numelY),... 45 | % reshape(data_nf.y,numelX,numelY),... 46 | % reshape(angle(data_nf.E(:,3))*180/pi,numelX,numelY)) 47 | % title(['Scan ' num2str(numelX) 'x' num2str(numelY) ' Phase Z Component']) 48 | 49 | end 50 | 51 | -------------------------------------------------------------------------------- /plot_functions/sphere3d.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hbartle/nf2ff_transformation/01641d29cb3b662686bef766f21fd8c5309eb007/plot_functions/sphere3d.m -------------------------------------------------------------------------------- /transformation_functions/nf2ff_cylindrical_fft.m: -------------------------------------------------------------------------------- 1 | function [data_nf2ff] = nf2ff_cylindrical_fft(data_nf,f,theta_range,phi_range,window) 2 | 3 | % Wave Number 4 | lambda = physconst('LightSpeed')/f; 5 | k0 = 2*pi/lambda; 6 | 7 | % Hankel Function of second kind and its derivative 8 | H = @(nu,Z) besselh(nu,2,Z); 9 | dH = @(nu,Z) 0.5*(besselh(nu-1,2,Z) - besselh(nu+1,2,Z)); 10 | 11 | % Cylinder Radius 12 | r0 = mean(data_nf.r); 13 | 14 | % Number of Samples in z dimension 15 | M = numel(unique(data_nf.z)); 16 | % Number of Samples in phi dimension 17 | N = length(data_nf.phi)/M; 18 | 19 | % Step size height dimension 20 | delta_z = (max(data_nf.z)-min(data_nf.z))/(M-1); 21 | % Step size angular dimension 22 | delta_phi = 2*pi/N; 23 | 24 | n = -N/2:(N/2-1); 25 | m = -M/2:(M/2-1); 26 | delta_kz =2*pi/(M*delta_z); 27 | kz = m*delta_kz; 28 | 29 | [k_grid, n_grid] = meshgrid(kz,n); 30 | 31 | % Hankel Coefficients 32 | h = 1/(k0*r0)*n_grid.*k_grid .* H(n_grid,sqrt(k0^2 - k_grid.^2)*r0); 33 | dh = -dH(n_grid,sqrt(k0^2 - k_grid.^2)*r0); 34 | h2 = 1/k0 * sqrt(k0^2 - k_grid.^2) .* H(n_grid,sqrt(k0^2 - k_grid.^2)*r0); 35 | 36 | % Reshape Efield components 37 | Ephi = reshape(data_nf.E(:,2),N,M); 38 | Ez = reshape(data_nf.E(:,3),N,M); 39 | 40 | % Sampling Window Function 41 | [Lphi,Lz] = size(Ephi); 42 | switch window 43 | case 'none' 44 | w_phi = ones(Lphi,1); 45 | w_z = ones(Lz,1); 46 | case 'tukey' 47 | w_phi = tukeywin(Lphi,1); 48 | w_z = tukeywin(Lz,1); 49 | case 'hamming' 50 | w_phi = hamming(Lphi); 51 | w_z = hamming(Lz); 52 | otherwise 53 | error('No valid window function selected') 54 | 55 | end 56 | 57 | W = w_phi*w_z'; 58 | 59 | % Spectral analysis 60 | b = 1./(h2.*delta_kz).*fftshift(fft(ifftshift(ifft(Ez.*W,[],1),1),[],2),2); 61 | a = 1./(dh.*delta_kz).*(b.*h.*delta_kz - fftshift(fft(ifftshift(ifft(Ephi.*W,[],1),1),[],2),2)); 62 | 63 | % b = 1./(h2.*delta_kz).*fftshift(fft(ifft(Ez.*W,[],1),[],2)); 64 | % a = 1./(dh.*delta_kz).*(b.*h.*delta_kz - fftshift(fft(ifft(Ephi.*W,[],1),[],2))); 65 | 66 | 67 | % Spherical Far-Field Wavenumber Vector 68 | [theta_grid,n_grid_spherical]=meshgrid(theta_range,n); 69 | kz_grid_spherical = k0*cos(theta_grid); 70 | 71 | a_ff=interp2(n_grid',k_grid',a',n_grid_spherical',kz_grid_spherical','spline')'; 72 | b_ff=interp2(n_grid',k_grid',b',n_grid_spherical',kz_grid_spherical','spline')'; 73 | 74 | 75 | % Far-Field Amplitude Factor 76 | r = 100; 77 | C = -2*k0*exp(-1j*k0*r)/r; 78 | alpha_spherical = sqrt(k0^2-kz_grid_spherical.^2); 79 | 80 | % Compute Far-Field 81 | % 82 | Etheta = (fft(1j*C*alpha_spherical.*1j.^n_grid_spherical.*b_ff)); 83 | Ephi = (fft(C*alpha_spherical.*1j.^n_grid_spherical.*a_ff)); 84 | Eabs = abs(sqrt(Etheta.^2 + Ephi.^2)); 85 | 86 | 87 | 88 | 89 | 90 | % w = tukeywin(size(b_ff,1),0.3); 91 | % Etheta = 1j*C*alpha_spherical.*(fft(1j.^n_grid_spherical.*b_ff,[],1)); 92 | % Ephi = C*alpha_spherical.*(fft(1j.^n_grid_spherical.*a_ff,[],1)); 93 | % Eabs = abs(sqrt(Etheta.^2 + Ephi.^2)); 94 | 95 | % Create Results Table 96 | p = N; 97 | t = length(theta_range); 98 | 99 | data_nf2ff = table(zeros(p*t,1),zeros(p*t,1),zeros(p*t,1),zeros(p*t,1),zeros(p*t,1)); 100 | data_nf2ff.Properties.VariableNames = {'theta','phi','Etheta','Ephi','Eabs'}; 101 | 102 | s = numel(theta_grid); 103 | data_nf2ff.theta = reshape(theta_grid,s,1); 104 | data_nf2ff.phi = reshape(delta_phi*(n_grid_spherical+abs(min(n_grid_spherical))),s,1); 105 | data_nf2ff.Etheta = reshape(Etheta,s,1); 106 | data_nf2ff.Ephi = reshape(Ephi,s,1); 107 | data_nf2ff.Eabs = reshape(Eabs,s,1); 108 | 109 | 110 | end 111 | 112 | 113 | -------------------------------------------------------------------------------- /transformation_functions/nf2ff_cylindrical_manual.m: -------------------------------------------------------------------------------- 1 | function [data_nf2ff] = nf2ff_cylindrical_manual(data_nf,f,theta_range,phi_range,window) 2 | 3 | % Hankel Function of second kind and its derivative 4 | H = @(nu,Z) besselh(nu,2,Z); 5 | dH = @(nu,Z) 0.5*(besselh(nu-1,2,Z) - besselh(nu+1,2,Z)); 6 | 7 | % Wave Number 8 | lambda = physconst('LightSpeed')/f; 9 | k0 = 2*pi/lambda; 10 | 11 | Ez = data_nf.E(:,3); 12 | Ephi = data_nf.E(:,2); 13 | 14 | % Cylinder Radius 15 | r0 = mean(data_nf.r)-0.15; 16 | % Number of Samples in z dimension 17 | M = numel(unique(data_nf.z)); 18 | % Number of Samples in phi dimension 19 | N = length(data_nf.phi)/M; 20 | 21 | % Step size height dimension 22 | delta_z = (max(data_nf.z)-min(data_nf.z))/(M-1); 23 | % Step size angular dimension 24 | delta_phi = 2*pi/N; 25 | 26 | 27 | % Calculate kz 28 | kz = k0*cos(theta_range); 29 | delta_kz = diff(kz); 30 | delta_kz = [2*delta_kz(1)-delta_kz(2) delta_kz]; 31 | delta_kz =repelem(2*pi/(M*delta_z),length(kz)); 32 | 33 | % Sampling Window Function 34 | [Lphi,Lz] = size(Ephi); 35 | switch window 36 | case 'none' 37 | w_phi = ones(Lphi,1); 38 | w_z = ones(Lz,1); 39 | case 'tukey' 40 | w_phi = tukeywin(Lphi,0.3); 41 | w_z = tukeywin(Lz,0.3); 42 | case 'hamming' 43 | w_phi = hamming(Lphi); 44 | w_z = hamming(Lz); 45 | otherwise 46 | error('No valid window function selected') 47 | 48 | end 49 | 50 | W = w_phi*w_z'; 51 | 52 | % Calculate spectral coefficients 53 | n = -N/2:N/2-1; 54 | for n_iter = 1:N 55 | for m=1:length(kz) 56 | alpha =sqrt(k0^2 - kz(m)); 57 | b(n_iter,m) = 1/(alpha/k0*H(n(n_iter),alpha*r0)*delta_kz(m)) *... 58 | (Ez.*W)'*exp(-1j*n(n_iter)*data_nf.phi + 1j*kz(m)*data_nf.z); 59 | a(n_iter,m) = 1/(alpha*dH(n(n_iter),alpha*r0)*delta_kz(m))... 60 | *(b(n_iter,m)*n(n_iter)*kz(m)/(k0*r0)*H(n(n_iter),alpha*r0)*delta_kz(m)... 61 | -(Ephi.*W)'*exp(-1j*n(n_iter)*data_nf.phi + 1j*kz(m)*data_nf.z)); 62 | end 63 | end 64 | 65 | 66 | % Far-Field Amplitude Factor 67 | r = 100; 68 | C = -2*k0*exp(-1j*k0*r)/r; 69 | % Compute Far-Field 70 | for n_iter=1:length(phi_range) 71 | for m=1:length(kz) 72 | alpha =sqrt(k0^2 - kz(m)); 73 | Etheta_ff(n_iter,m) = 1j*alpha*C* (1j.^n.*b(:,m)')*exp(1j*n*phi_range(n_iter))'; 74 | Ephi_ff(n_iter,m) = alpha*C* (1j.^n.*a(:,m)')*exp(1j*n*phi_range(n_iter))'; 75 | 76 | Eabs(n_iter,m) = abs(sqrt(Etheta_ff(n_iter,m)^2 + Ephi_ff(n_iter,m)^2)); 77 | end 78 | end 79 | 80 | % Rearrange E-fields to have proper alignment in phi dimension 81 | Etheta_ff = [Etheta_ff(end/2+1:end,:);Etheta_ff(1:end/2,:)]; 82 | Ephi_ff = [Ephi_ff(end/2+1:end,:);Ephi_ff(1:end/2,:)]; 83 | Eabs = [Eabs(end/2+1:end,:);Eabs(1:end/2,:)]; 84 | 85 | 86 | % Create Results Table 87 | s = numel(Etheta_ff); 88 | data_nf2ff = table(zeros(s,1),zeros(s,1),zeros(s,1),zeros(s,1),zeros(s,1)); 89 | data_nf2ff.Properties.VariableNames = {'theta','phi','Etheta','Ephi','Eabs'}; 90 | 91 | [t,p] = meshgrid(acos(kz/k0),phi_range); 92 | data_nf2ff.theta = reshape(t,s,1); 93 | data_nf2ff.phi = reshape(p,s,1); 94 | data_nf2ff.Etheta = reshape(Etheta_ff,s,1); 95 | data_nf2ff.Ephi = reshape(Ephi_ff,s,1); 96 | data_nf2ff.Eabs = reshape(Eabs,s,1); 97 | 98 | 99 | 100 | end 101 | 102 | 103 | -------------------------------------------------------------------------------- /transformation_functions/nf2ff_planar_fft.m: -------------------------------------------------------------------------------- 1 | function [data_nf2ff] = nf2ff_planar_fft(data_nf,f,phi_range,theta_range,padding,window) 2 | 3 | % Create Results Table 4 | p = length(phi_range); 5 | t = length(theta_range); 6 | 7 | data_nf2ff = table(zeros(p*t,1),zeros(p*t,1),zeros(p*t,3),zeros(p*t,1),zeros(p*t,1),zeros(p*t,1)); 8 | data_nf2ff.Properties.VariableNames = {'theta','phi','E','Etheta','Ephi','Eabs'}; 9 | 10 | 11 | % Step size in x,y direction [m] 12 | delta_x = unique(data_nf.x); 13 | number_of_samples_x = numel(delta_x); 14 | delta_x = abs(delta_x(1)-delta_x(2)); 15 | delta_y = unique(data_nf.y); 16 | number_of_samples_y = numel(delta_y); 17 | delta_y = abs(delta_y(1)-delta_y(2)); 18 | 19 | % Scanner Plane Dimensions 20 | length_x = delta_x * (number_of_samples_x-1); 21 | length_y = delta_y * (number_of_samples_y-1); 22 | z0 = mean(data_nf.z); 23 | 24 | 25 | % Rectangular Wavenumber Vector as according to Balanis 26 | lambda=physconst('lightspeed')/f; 27 | k0=2*pi/lambda; 28 | 29 | 30 | number_of_samples_x_padded = padding*number_of_samples_x; 31 | number_of_samples_y_padded = padding*number_of_samples_y; 32 | m= -number_of_samples_x_padded/2:1:number_of_samples_x_padded/2-1; 33 | n= -number_of_samples_y_padded/2:1:number_of_samples_y_padded/2-1; 34 | 35 | kx=2*pi*m/(number_of_samples_x_padded*delta_x); 36 | ky=2*pi*n/(number_of_samples_y_padded*delta_y); 37 | [ky_grid,kx_grid] = meshgrid(ky,kx); 38 | kz_grid = sqrt(k0^2-kx_grid.^2-ky_grid.^2); 39 | 40 | % Spherical Far-Field Wavenumber Vector 41 | [theta_grid,phi_grid]=meshgrid(theta_range,phi_range); 42 | kx_grid_spherical = k0*sin(theta_grid).*cos(phi_grid); 43 | ky_grid_spherical = k0*sin(theta_grid).*sin(phi_grid); 44 | kz_grid_spherical = k0*cos(theta_grid); 45 | 46 | 47 | % Reshape E field to fit grid 48 | Ex_nf = reshape(data_nf.E(:,1),number_of_samples_x,number_of_samples_y); 49 | Ey_nf = reshape(data_nf.E(:,2),number_of_samples_x,number_of_samples_y); 50 | Ez_nf = reshape(data_nf.E(:,3),number_of_samples_x,number_of_samples_y); 51 | 52 | 53 | % Sampling Window Function 54 | L = length(Ex_nf); 55 | 56 | switch window 57 | case 'none' 58 | h = ones(L); 59 | case 'tukey' 60 | h = tukeywin(L); 61 | case 'hamming' 62 | h = hamming(L); 63 | otherwise 64 | disp('No valid window function selected') 65 | end 66 | 67 | H = h*h'/L; 68 | 69 | 70 | % Retrieve Plane Wave Modes through FFT 71 | fx=ifftshift(ifft2(Ex_nf.*H,number_of_samples_x_padded,number_of_samples_y_padded)); 72 | fy=ifftshift(ifft2(Ey_nf.*H,number_of_samples_x_padded,number_of_samples_y_padded)); 73 | fz=-(fx.*kx_grid+fy.*ky_grid)./kz_grid; 74 | 75 | 76 | % Interpolate Modes in spherical coordinates 77 | fx_ff=interp2(kx,ky,abs(fx),kx_grid_spherical,ky_grid_spherical,'spline'); 78 | fy_ff=interp2(kx,ky,abs(fy),kx_grid_spherical,ky_grid_spherical,'spline'); 79 | fz_ff=interp2(kx,ky,abs(fz),kx_grid_spherical,ky_grid_spherical,'spline'); 80 | 81 | % Far Field 82 | r=100; 83 | C=1j*(k0*exp(-1j*k0*r))/(2*pi*r); 84 | 85 | Etheta=C*(fx_ff.*cos(phi_grid)+fy_ff.*sin(phi_grid)); 86 | Ephi=C*cos(theta_grid).*(-fx_ff.*sin(phi_grid)+fy_ff.*cos(phi_grid)); 87 | Ex = C*cos(theta_grid).*fx_ff; 88 | Ey = C*cos(theta_grid).*fy_ff; 89 | Ez = C*cos(theta_grid).*fz_ff; 90 | 91 | % Fill Results Table 92 | s = numel(theta_grid); 93 | data_nf2ff.Etheta = reshape(Etheta,s,1); 94 | data_nf2ff.Ephi = reshape(Ephi,s,1); 95 | data_nf2ff.E = [reshape(Ex,s,1),reshape(Ey,s,1),reshape(Ez,s,1)]; 96 | data_nf2ff.Eabs = vecnorm(data_nf2ff.E,2,2); 97 | data_nf2ff.phi = reshape(phi_grid,s,1); 98 | data_nf2ff.theta= reshape(theta_grid,s,1); 99 | 100 | end 101 | 102 | 103 | -------------------------------------------------------------------------------- /transformation_functions/nf2ff_planar_manual.m: -------------------------------------------------------------------------------- 1 | function [data_nf2ff] = nf2ff_planar_manual(data_nf,f,phi_range,theta_range,window) 2 | 3 | 4 | % Create Results Table 5 | p = length(phi_range); 6 | t = length(theta_range); 7 | 8 | data_nf2ff = table(zeros(p*t,1),zeros(p*t,1),zeros(p*t,3),zeros(p*t,1)); 9 | data_nf2ff.Properties.VariableNames = {'theta','phi','E','Eabs'}; 10 | 11 | 12 | % Wave Number 13 | lambda = physconst('LightSpeed')/f; 14 | k_const = 2*pi/lambda; 15 | 16 | % Far-Field Point Radius [m] 17 | %r = 2*pi; 18 | r = 1; 19 | % Calculate k vector for each far field direction 20 | n = 1; 21 | for phi= phi_range 22 | for theta = theta_range 23 | k(n,:) = k_const*[sin(theta)*cos(phi); sin(theta)*sin(phi); cos(theta)]; 24 | 25 | % Fill table with angle values 26 | data_nf2ff.theta(n) = theta; 27 | data_nf2ff.phi(n) = phi; 28 | 29 | n =n+1; 30 | end 31 | end 32 | 33 | 34 | % Get step size in x,y direction 35 | delta_x = unique(data_nf.x); 36 | delta_x = abs(delta_x(1)-delta_x(2)); 37 | delta_y = unique(data_nf.y); 38 | delta_y = abs(delta_y(1)-delta_y(2)); 39 | 40 | % Sampling Window Function 41 | L = length(unique(data_nf.x)); 42 | 43 | switch window 44 | case 'none' 45 | h = ones(L); 46 | case 'tukey' 47 | h = tukeywin(L); 48 | case 'hamming' 49 | h = hamming(L); 50 | otherwise 51 | disp('No valid window function selected') 52 | end 53 | 54 | H = reshape(h*h'/L,length(data_nf.Eabs),1); 55 | Ex = data_nf.E(:,1).*H; 56 | Ey = data_nf.E(:,2).*H; 57 | 58 | 59 | % Create A matrix 60 | % A = delta_x*delta_y * exp(1j*k*[data_nf.x,data_nf.y,data_nf.z]'); 61 | A = delta_x*delta_y * exp(1j*k(:,1:2)*[data_nf.x,data_nf.y]'); 62 | 63 | % Calculate Plane Wave Modal Coefficients 64 | Fx = A*Ex; 65 | Fy = A*Ey; 66 | % Fz = A*data_nf.E(:,3); 67 | Fz = -(k(:,1).*Fx + k(:,2).*Fy)./k(:,3); 68 | 69 | % Calculate far-field E 70 | data_nf2ff.E = 1j * exp(-1j*k_const*r)/(lambda*r)*1/k_const* repmat(k(:,3),1,3) .* [Fx Fy Fz]; 71 | 72 | 73 | data_nf2ff.Eabs = vecnorm(abs(data_nf2ff.E),2,2); 74 | 75 | 76 | end 77 | 78 | 79 | -------------------------------------------------------------------------------- /transformation_functions/nf2ff_spherical_manual.m: -------------------------------------------------------------------------------- 1 | function [data_nf2ff] = nf2ff_spherical_manual(data_nf,f,theta_range,phi_range) 2 | 3 | % Wave Number 4 | lambda = physconst('LightSpeed')/f; 5 | k0 = 2*pi/lambda; 6 | % Antenna minimal sphere radius 7 | r0 = 2/3*mean(data_nf.r); 8 | % Radius of Measurement Sphere 9 | A = mean(data_nf.r); 10 | 11 | theta = data_nf.theta; 12 | phi = data_nf.phi; 13 | Etheta = data_nf.E(:,2); 14 | Ephi = data_nf.E(:,3); 15 | 16 | % Step Sizes 17 | delta_theta = diff(unique(round(data_nf.theta,2))); 18 | delta_theta = delta_theta(1); 19 | delta_phi = diff(unique(round(data_nf.phi,2))); 20 | delta_phi = delta_phi(1); 21 | 22 | % Theta/Phi values of Far-field points to calculate 23 | [theta_ff,phi_ff] = meshgrid(theta_range,phi_range); 24 | theta_ff = reshape(theta_ff,numel(theta_ff),1); 25 | phi_ff = reshape(phi_ff,numel(phi_ff),1); 26 | % Far-Field radius 27 | r_ff = 10; 28 | 29 | 30 | % Number of spherical wave modes 31 | N = round(k0*r0 + 5); 32 | 33 | % Preallocate space for increased speed 34 | Etheta_ff = zeros(size(theta_ff)); 35 | Ephi_ff = zeros(size(phi_ff)); 36 | 37 | idx = 1; 38 | for s=1:2 39 | for n = 1:N 40 | M = repmat((-n:n),length(theta),1); 41 | % Compute Spherical Expansion Coefficients 42 | [~,ftheta,fphi,Y] = sphericalVectorWaveFunction(s,M,n,A,theta,phi,k0); 43 | ftheta_t = conj(ftheta).*sin(theta); 44 | fphi_t = conj(fphi).*sin(theta); 45 | q = (1./Y .*(Etheta'*ftheta_t + Ephi'*fphi_t))'*delta_theta*delta_phi; 46 | 47 | % Compute Far-Field 48 | M = repmat((-n:n),length(theta_ff),1); 49 | [xtheta,xphi] = sphericalVectorWaveFunctionFarField(s,M,n,r_ff,theta_ff,phi_ff,k0); 50 | 51 | Etheta_ff = Etheta_ff + sum(repmat(q,1,length(theta_ff))'.*xtheta,2); 52 | Ephi_ff = Ephi_ff + sum(repmat(q,1,length(theta_ff))'.*xphi,2); 53 | 54 | idx = idx+1 55 | end 56 | end 57 | 58 | 59 | % Create Results Table 60 | s = numel(Etheta_ff); 61 | data_nf2ff = table(zeros(s,1),zeros(s,1),zeros(s,1),zeros(s,1),zeros(s,1)); 62 | data_nf2ff.Properties.VariableNames = {'theta','phi','Etheta','Ephi','Eabs'}; 63 | 64 | 65 | data_nf2ff.theta = reshape(t,s,1); 66 | data_nf2ff.phi = reshape(p,s,1); 67 | data_nf2ff.Etheta = reshape(Etheta_ff,s,1); 68 | data_nf2ff.Ephi = reshape(Ephi_ff,s,1); 69 | data_nf2ff.Eabs = reshape(Eabs,s,1); 70 | 71 | 72 | 73 | end 74 | 75 | 76 | --------------------------------------------------------------------------------