|
@@ -58,6 +58,27 @@ function [AF, FS] = sphear_af_read(measurements, raw = false);
|
|
|
AF(m, c, :) = A;
|
|
AF(m, c, :) = A;
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
|
|
+ %% read any up/down measurements
|
|
|
|
|
+ if (exist([DATA CURRENT "af/a-format-cal-up-001-5.wav"]))
|
|
|
|
|
+ for m = measurements
|
|
|
|
|
+ %% read all capsules
|
|
|
|
|
+ for c = 1:capsules
|
|
|
|
|
+ %% read in calibrated capsule measurement
|
|
|
|
|
+ [A, FS] = wavread([DATA CURRENT "af/a-format-" type sprintf("up-%03d", m) "-" char(capnames(c)) ".wav"]);
|
|
|
|
|
+ AF(m + 16, c, :) = A;
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+ if (exist([DATA CURRENT "af/a-format-cal-down-001-5.wav"]))
|
|
|
|
|
+ for m = measurements
|
|
|
|
|
+ %% read all capsules
|
|
|
|
|
+ for c = 1:capsules
|
|
|
|
|
+ %% read in calibrated capsule measurement
|
|
|
|
|
+ [A, FS] = wavread([DATA CURRENT "af/a-format-" type sprintf("down-%03d", m) "-" char(capnames(c)) ".wav"]);
|
|
|
|
|
+ AF(m + 16 + 16, c, :) = A;
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
%%
|
|
%%
|
|
@@ -98,21 +119,52 @@ function [R] = sphear_signal_power(SIG, FS, fmin, fmax, fft_size, cardioid = 0,
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
+%%
|
|
|
|
|
+%% Calculate power for capsule in a frequency range
|
|
|
|
|
+%%
|
|
|
|
|
+
|
|
|
|
|
+function [R] = sphear_capsule_power(SIG, FS, fmin, fmax, fft_size)
|
|
|
|
|
+ %% indexes in array for frequency bins
|
|
|
|
|
+ imin = round((fft_size/2) * fmin / (FS/2));
|
|
|
|
|
+ imax = round((fft_size/2) * fmax / (FS/2));
|
|
|
|
|
+ measurements = size(SIG)(1);
|
|
|
|
|
+ signals = size(SIG,2);
|
|
|
|
|
+ %% calculate power in frequency range
|
|
|
|
|
+ for m = (1:measurements)
|
|
|
|
|
+ for s = (1:signals)
|
|
|
|
|
+ spectrum = fft(SIG(m, s, :), fft_size);
|
|
|
|
|
+ %% return total power of signal in frequency range
|
|
|
|
|
+ R(m,s,:) = sqrt(sum(spectrum(imin:imax).*conj(spectrum(imin:imax)))/(imax - imin));
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
%%
|
|
%%
|
|
|
%% Calculate A to B matrix from matrix of power measurements
|
|
%% Calculate A to B matrix from matrix of power measurements
|
|
|
%%
|
|
%%
|
|
|
|
|
|
|
|
-function [A2B PV COND ERROR] = sphear_calculate_a2b(R)
|
|
|
|
|
- [A2B PV ERROR] = A2B_matrix(R);
|
|
|
|
|
|
|
+function [A2B PV COND ERROR] = sphear_calculate_a2b(R, M)
|
|
|
|
|
+ if (exist("M"))
|
|
|
|
|
+ [A2B PV ERROR] = A2B_matrix(R, M);
|
|
|
|
|
+ else
|
|
|
|
|
+ [A2B PV ERROR] = A2B_matrix(R);
|
|
|
|
|
+ end
|
|
|
%% normalize matrix
|
|
%% normalize matrix
|
|
|
%%
|
|
%%
|
|
|
%% normalize first order components of the matrix first
|
|
%% normalize first order components of the matrix first
|
|
|
A2B = A2B ./ mean(mean(abs(A2B(:,1:4))));
|
|
A2B = A2B ./ mean(mean(abs(A2B(:,1:4))));
|
|
|
if (size(A2B, 2) > 4)
|
|
if (size(A2B, 2) > 4)
|
|
|
- %% U/V are difference microphones, so at the reference
|
|
|
|
|
|
|
+ %% S/T/U/V are difference microphones, so at the reference
|
|
|
%% frequency range their signal is much lower than the
|
|
%% frequency range their signal is much lower than the
|
|
|
%% first order components, so normalize separately
|
|
%% first order components, so normalize separately
|
|
|
- A2B(:,5:6) = A2B(:,5:6) ./ mean(mean(abs(A2B(:,5:6))));
|
|
|
|
|
|
|
+ ST_avg = mean(mean(abs(A2B(:,5:6))));
|
|
|
|
|
+ if (ST_avg != 0)
|
|
|
|
|
+ A2B(:,5:6) = A2B(:,5:6) ./ ST_avg;
|
|
|
|
|
+ end
|
|
|
|
|
+ UV_avg = mean(mean(abs(A2B(:,7:8))));
|
|
|
|
|
+ if (UV_avg != 0)
|
|
|
|
|
+ A2B(:,7:8) = A2B(:,7:8) ./ UV_avg;
|
|
|
|
|
+ end
|
|
|
end
|
|
end
|
|
|
%% return condition number
|
|
%% return condition number
|
|
|
COND = cond(A2B);
|
|
COND = cond(A2B);
|
|
@@ -135,7 +187,7 @@ function [BF, BFF] = sphear_af_to_bf(AF, A2B, FIR, fft_size);
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
if exist("FIR")
|
|
if exist("FIR")
|
|
|
- if (size(FIR, 1) <= 6)
|
|
|
|
|
|
|
+ if (size(FIR, 1) <= 8)
|
|
|
%% filter the B format signal through the common FIR filters
|
|
%% filter the B format signal through the common FIR filters
|
|
|
for m = (1:measurements)
|
|
for m = (1:measurements)
|
|
|
for b = 1:size(FIR, 1)
|
|
for b = 1:size(FIR, 1)
|
|
@@ -225,7 +277,7 @@ function [SP, FC, FE] = sphear_signal_power_all(SIG, FS, fmin, fmax, frefmin, fr
|
|
|
ref = sphear_signal_power(SIG, FS, frefmin, frefmax, fft_size);
|
|
ref = sphear_signal_power(SIG, FS, frefmin, frefmax, fft_size);
|
|
|
norm = mean(ref(:,:));
|
|
norm = mean(ref(:,:));
|
|
|
|
|
|
|
|
- %% we only normalize with respect to WXYZ, UV in the frequency range
|
|
|
|
|
|
|
+ %% we only normalize with respect to WXYZ, STUV in the frequency range
|
|
|
%% where WXYZ are well behaved has low power (high pass filter due
|
|
%% where WXYZ are well behaved has low power (high pass filter due
|
|
|
%% to the difference microphones) so we don't want to average that.
|
|
%% to the difference microphones) so we don't want to average that.
|
|
|
%% If we do that the A2B coefficients are much higher than the ones
|
|
%% If we do that the A2B coefficients are much higher than the ones
|
|
@@ -233,8 +285,8 @@ function [SP, FC, FE] = sphear_signal_power_all(SIG, FS, fmin, fmax, frefmin, fr
|
|
|
%% then attenuation in the filtering stage after that)
|
|
%% then attenuation in the filtering stage after that)
|
|
|
WXYZnorm = mean(ref(:,[1,2,3,4]));
|
|
WXYZnorm = mean(ref(:,[1,2,3,4]));
|
|
|
XYavg = mean(WXYZnorm([2,3]));
|
|
XYavg = mean(WXYZnorm([2,3]));
|
|
|
- %% use XY average for UV
|
|
|
|
|
- norm = cat(1, WXYZnorm', [XYavg], [XYavg])';
|
|
|
|
|
|
|
+ %% use XY average for STUV
|
|
|
|
|
+ norm = cat(1, WXYZnorm', [XYavg], [XYavg], [XYavg], [XYavg])';
|
|
|
|
|
|
|
|
for i = (1:size(FC))
|
|
for i = (1:size(FC))
|
|
|
%% (measurement,signal,band,power)
|
|
%% (measurement,signal,band,power)
|
|
@@ -252,7 +304,7 @@ end
|
|
|
%% 2 -> use cardinal measurements
|
|
%% 2 -> use cardinal measurements
|
|
|
%% 3 -> use diagonal measurements
|
|
%% 3 -> use diagonal measurements
|
|
|
|
|
|
|
|
-function [W X Y Z U V] = sphear_bf_extract_filter_shapes(B, measurements)
|
|
|
|
|
|
|
+function [W X Y Z S T U V] = sphear_bf_extract_filter_shapes(B, measurements)
|
|
|
%% cardinal and diagonal directions for four capsule array
|
|
%% cardinal and diagonal directions for four capsule array
|
|
|
TETRA_CARDINALS = [1, 5, 9, 13];
|
|
TETRA_CARDINALS = [1, 5, 9, 13];
|
|
|
TETRA_DIAGONALS = [3, 7, 11, 15];
|
|
TETRA_DIAGONALS = [3, 7, 11, 15];
|
|
@@ -262,9 +314,14 @@ function [W X Y Z U V] = sphear_bf_extract_filter_shapes(B, measurements)
|
|
|
%% best directions for octa WXY
|
|
%% best directions for octa WXY
|
|
|
OCTA_FLAT = OCTA_DIAGONALS;
|
|
OCTA_FLAT = OCTA_DIAGONALS;
|
|
|
OCTA_BRIGHT = OCTA_CARDINALS;
|
|
OCTA_BRIGHT = OCTA_CARDINALS;
|
|
|
|
|
+ %% best directions for octa Z
|
|
|
|
|
+ OCTA_FLAT_Z = [OCTA_FLAT + 16, OCTA_FLAT + 32];
|
|
|
%% best directions for octa UV
|
|
%% best directions for octa UV
|
|
|
U_DIRS = [1, 5, 9, 13];
|
|
U_DIRS = [1, 5, 9, 13];
|
|
|
V_DIRS = [3, 7, 11, 15];
|
|
V_DIRS = [3, 7, 11, 15];
|
|
|
|
|
+ %% best directions for octa ST (up and down layers)
|
|
|
|
|
+ S_DIRS = [U_DIRS + 16, U_DIRS + 32];
|
|
|
|
|
+ T_DIRS = [V_DIRS + 16, V_DIRS + 32];
|
|
|
%% all directions
|
|
%% all directions
|
|
|
ALL_DIRS = [1:16];
|
|
ALL_DIRS = [1:16];
|
|
|
|
|
|
|
@@ -273,10 +330,19 @@ function [W X Y Z U V] = sphear_bf_extract_filter_shapes(B, measurements)
|
|
|
if (size(B, 2) > 4)
|
|
if (size(B, 2) > 4)
|
|
|
%% 8 capsules
|
|
%% 8 capsules
|
|
|
%% averaging all measurements for W seems to do better
|
|
%% averaging all measurements for W seems to do better
|
|
|
|
|
+ %%W_m = [OCTA_FLAT, OCTA_FLAT + 16, OCTA_FLAT + 32];
|
|
|
|
|
+ %%Y_m = [OCTA_FLAT, OCTA_FLAT + 16, OCTA_FLAT + 32];
|
|
|
|
|
+ %%X_m = [OCTA_FLAT, OCTA_FLAT + 16, OCTA_FLAT + 32];
|
|
|
W_m = OCTA_FLAT;
|
|
W_m = OCTA_FLAT;
|
|
|
Y_m = OCTA_FLAT;
|
|
Y_m = OCTA_FLAT;
|
|
|
X_m = OCTA_FLAT;
|
|
X_m = OCTA_FLAT;
|
|
|
- Z_m = W_m;
|
|
|
|
|
|
|
+ if (size(B, 1) > 16)
|
|
|
|
|
+ Z_m = OCTA_FLAT_Z;
|
|
|
|
|
+ else
|
|
|
|
|
+ Z_m = W_m;
|
|
|
|
|
+ end
|
|
|
|
|
+ S_m = S_DIRS;
|
|
|
|
|
+ T_m = T_DIRS;
|
|
|
U_m = U_DIRS;
|
|
U_m = U_DIRS;
|
|
|
V_m = V_DIRS;
|
|
V_m = V_DIRS;
|
|
|
else
|
|
else
|
|
@@ -323,8 +389,17 @@ function [W X Y Z U V] = sphear_bf_extract_filter_shapes(B, measurements)
|
|
|
Y = 1.0 ./ reshape(mean(B(Y_m,3,:)), size(B(:,:,:))(3), 1);
|
|
Y = 1.0 ./ reshape(mean(B(Y_m,3,:)), size(B(:,:,:))(3), 1);
|
|
|
Z = 1.0 ./ reshape(mean(B(Z_m,4,:)), size(B(:,:,:))(3), 1);
|
|
Z = 1.0 ./ reshape(mean(B(Z_m,4,:)), size(B(:,:,:))(3), 1);
|
|
|
if (size(B, 2) > 4)
|
|
if (size(B, 2) > 4)
|
|
|
- U = 1.0 ./ reshape(mean(B(U_m,5,:)), size(B(:,:,:))(3), 1);
|
|
|
|
|
- V = 1.0 ./ reshape(mean(B(V_m,6,:)), size(B(:,:,:))(3), 1);
|
|
|
|
|
|
|
+ if (size(B, 1) > 16)
|
|
|
|
|
+ %% full 3d measurements
|
|
|
|
|
+ S = 1.0 ./ reshape(mean(B(S_m,5,:)), size(B(:,:,:))(3), 1);
|
|
|
|
|
+ T = 1.0 ./ reshape(mean(B(T_m,6,:)), size(B(:,:,:))(3), 1);
|
|
|
|
|
+ else
|
|
|
|
|
+ %% flat 2d measurements, no S/T filters (ie: flat 1.0 filter)
|
|
|
|
|
+ S = 1.0 ./ ones(size(B, 3), 1);
|
|
|
|
|
+ T = 1.0 ./ ones(size(B, 3), 1);
|
|
|
|
|
+ end
|
|
|
|
|
+ U = 1.0 ./ reshape(mean(B(U_m,7,:)), size(B(:,:,:))(3), 1);
|
|
|
|
|
+ V = 1.0 ./ reshape(mean(B(V_m,8,:)), size(B(:,:,:))(3), 1);
|
|
|
end
|
|
end
|
|
|
end
|
|
end
|
|
|
|
|
|
|
@@ -332,31 +407,72 @@ end
|
|
|
%% Calculate average power for each B format component and normalize A2B matrix
|
|
%% Calculate average power for each B format component and normalize A2B matrix
|
|
|
%%
|
|
%%
|
|
|
|
|
|
|
|
-function [A2B XY_gain, UV_gain] = sphear_bf_normalize_gain(A2B, B, FS, fmin, fmax, fft_size)
|
|
|
|
|
|
|
+function [A2B XY_gain, S_gain, T_gain, U_gain, V_gain] = sphear_bf_normalize_gain(A2B, B, FS, fmin, fmax, fft_size)
|
|
|
|
|
+ do_Z = false;
|
|
|
|
|
+ if (size(B,1)/3 > 8)
|
|
|
|
|
+ %% we assume we have three sets of measurements and can do Z averaging
|
|
|
|
|
+ do_Z = true;
|
|
|
|
|
+ end
|
|
|
%% (measurement,signal,band,power)
|
|
%% (measurement,signal,band,power)
|
|
|
B_power = sphear_signal_power(B, FS, fmin, fmax, fft_size);
|
|
B_power = sphear_signal_power(B, FS, fmin, fmax, fft_size);
|
|
|
%% W, all measurements
|
|
%% W, all measurements
|
|
|
W_avg = mean(B_power(:,1));
|
|
W_avg = mean(B_power(:,1));
|
|
|
%% XY, peak of first order lobes
|
|
%% XY, peak of first order lobes
|
|
|
XY_avg = mean([mean(B_power([1,9], 2)), mean(B_power([5, 13], 3))]);
|
|
XY_avg = mean([mean(B_power([1,9], 2)), mean(B_power([5, 13], 3))]);
|
|
|
|
|
+ XY_gain = XY_avg/W_avg;
|
|
|
|
|
+ if (do_Z)
|
|
|
|
|
+ %% Z, +-elev weighted by elevation angle
|
|
|
|
|
+ %% FIXME: we just hardwire the elevation angle for now
|
|
|
|
|
+ Z_avg = mean(B_power([17:32,33:48],4)) / sin(43/180*pi);
|
|
|
|
|
+ Z_gain = Z_avg/W_avg;
|
|
|
|
|
+ else
|
|
|
|
|
+ Z_gain = 0.0;
|
|
|
|
|
+ end
|
|
|
if (size(A2B, 2) > 4)
|
|
if (size(A2B, 2) > 4)
|
|
|
%% UV, peak of second order lobes
|
|
%% UV, peak of second order lobes
|
|
|
- UV_avg = mean([mean(B_power([1,5,9,13], 5)), mean(B_power([3,7,11,15], 6))]);
|
|
|
|
|
|
|
+ U_avg = mean(mean(B_power([1,5,9,13], 7)));
|
|
|
|
|
+ U_gain = U_avg/W_avg;
|
|
|
|
|
+ V_avg = mean(mean(B_power([3,7,11,15], 8)));
|
|
|
|
|
+ V_gain = V_avg/W_avg;
|
|
|
|
|
+ if (mean(mean(A2B(:,[5,6]))) > 0)
|
|
|
|
|
+ %% calculate peak value of s/t at measured elevation
|
|
|
|
|
+ el = 43.0;
|
|
|
|
|
+ s_t_val = 1.0 * sin((2*el)/180*pi);
|
|
|
|
|
+ s_t_val
|
|
|
|
|
+ %% ST, peaks of lobes at +-elevation
|
|
|
|
|
+ S_avg = mean([mean(B_power([1,9] + 16, 5)), mean(B_power([1,9] + 32, 5))]);
|
|
|
|
|
+ S_gain = (S_avg/W_avg) * s_t_val;
|
|
|
|
|
+ T_avg = mean([mean(B_power([5,13] + 16, 6)), mean(B_power([5, 13] + 32, 6))]);
|
|
|
|
|
+ T_gain = (T_avg/W_avg) * s_t_val;
|
|
|
|
|
+ else
|
|
|
|
|
+ S_gain = 0.0;
|
|
|
|
|
+ T_gain = 0.0;
|
|
|
|
|
+ end
|
|
|
|
|
+ else
|
|
|
|
|
+ U_gain = 0.0;
|
|
|
|
|
+ V_gain = 0.0;
|
|
|
|
|
+ end
|
|
|
|
|
+ %% normalize the A2B matrix
|
|
|
|
|
+ if (do_Z)
|
|
|
|
|
+ A2B(:,[2,3]) = A2B(:,[2,3]) / XY_gain;
|
|
|
|
|
+ A2B(:,[4]) = A2B(:,[4]) / Z_gain;
|
|
|
|
|
+ else
|
|
|
|
|
+ A2B(:,[2,3,4]) = A2B(:,[2,3,4]) / XY_gain;
|
|
|
end
|
|
end
|
|
|
- %% calculate the gain coefficients and normalize the A2B matrix
|
|
|
|
|
- XY_gain = XY_avg/W_avg;
|
|
|
|
|
- A2B(:,[2,3,4]) = A2B(:,[2,3,4]) / XY_gain;
|
|
|
|
|
if (size(A2B, 2) > 4)
|
|
if (size(A2B, 2) > 4)
|
|
|
- UV_gain = UV_avg/W_avg;
|
|
|
|
|
- A2B(:,[5,6]) = A2B(:,[5,6]) / UV_gain;
|
|
|
|
|
|
|
+ if (mean(mean(A2B(:,[5,6]))) > 0)
|
|
|
|
|
+ A2B(:,[5]) = A2B(:,[5]) / S_gain;
|
|
|
|
|
+ A2B(:,[6]) = A2B(:,[6]) / T_gain;
|
|
|
|
|
+ end
|
|
|
|
|
+ A2B(:,[7]) = A2B(:,[7]) / U_gain;
|
|
|
|
|
+ A2B(:,[8]) = A2B(:,[8]) / V_gain;
|
|
|
else
|
|
else
|
|
|
- UV_gain = 1.0;
|
|
|
|
|
end
|
|
end
|
|
|
%% we want the W contribution of each capsule to be 1/8,
|
|
%% we want the W contribution of each capsule to be 1/8,
|
|
|
%% so scale the whole matrix relative to mean W
|
|
%% so scale the whole matrix relative to mean W
|
|
|
A2B(:,:) = A2B(:,:) / (mean(A2B(:, 1)) * 8);
|
|
A2B(:,:) = A2B(:,:) / (mean(A2B(:, 1)) * 8);
|
|
|
%% print gain scaling coefficients
|
|
%% print gain scaling coefficients
|
|
|
- printf("XY gain: %f, UV gain: %f\n", XY_gain, UV_gain);
|
|
|
|
|
|
|
+ printf("XY gain: %f, Z gain: %f, ST: %f|%f, UV: %f|%f\n", XY_gain, Z_gain, S_gain, T_gain, U_gain, V_gain);
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
%%
|
|
%%
|
|
@@ -477,3 +593,174 @@ function sphear_plot_all_filters(FC, UC, fmin, fmax)
|
|
|
hold off;
|
|
hold off;
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
+%%%
|
|
|
|
|
+%%% Plot B format signal for three elevation measurement angles
|
|
|
|
|
+%%%
|
|
|
|
|
+
|
|
|
|
|
+function sphear_plot_measurements_3d(SIG, FC, FE, FS, N, freq)
|
|
|
|
|
+ figure;
|
|
|
|
|
+ hold on;
|
|
|
|
|
+ axis([1, 16]);
|
|
|
|
|
+ grid on;
|
|
|
|
|
+ ca=get (gcf, "currentaxes");
|
|
|
|
|
+ set(ca,"fontweight","bold","linewidth",1.5)
|
|
|
|
|
+ %% find the approximate desired frequency band
|
|
|
|
|
+ for f = [1:size(FC)]
|
|
|
|
|
+ if (FC(f) <= freq)
|
|
|
|
|
+ band = f;
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+ title(["B format power in frequency band (" sprintf("%.0f", FE(band)) " to " sprintf("%.0f", FE(band+1)) " Hz)"]);
|
|
|
|
|
+ %% and plot the powers
|
|
|
|
|
+ for i = [1,17,33]
|
|
|
|
|
+ if (i < 17)
|
|
|
|
|
+ wstyle = "ko-;W0;";
|
|
|
|
|
+ color = "r";
|
|
|
|
|
+ Xname = "X 0elev";
|
|
|
|
|
+ Yname = "Y 0elev";
|
|
|
|
|
+ else
|
|
|
|
|
+ if (i < 33)
|
|
|
|
|
+ wstyle = "ko--;WU;";
|
|
|
|
|
+ color = "b";
|
|
|
|
|
+ Xname = "X +elev";
|
|
|
|
|
+ Yname = "Y +elev";
|
|
|
|
|
+ else
|
|
|
|
|
+ wstyle = "ko:;WD;";
|
|
|
|
|
+ color = "g";
|
|
|
|
|
+ Xname = "X -elev";
|
|
|
|
|
+ Yname = "Y -elev";
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+ %% W
|
|
|
|
|
+ plot(SIG((i:i+15),1,band), wstyle, "linewidth", 1.5);
|
|
|
|
|
+ %% X/Y
|
|
|
|
|
+ plot(SIG((i:i+15),[2],band), "color", color, sprintf("o-;%s;", Xname), "linewidth", 1.5);
|
|
|
|
|
+ plot(SIG((i:i+15),[3],band), "color", color, sprintf("o-;%s;", Yname), "linewidth", 1.5);
|
|
|
|
|
+ %% Z
|
|
|
|
|
+ pZ = plot(SIG((i:i+15),4,band), "co-;Z;", "linewidth", 1.5);
|
|
|
|
|
+ end
|
|
|
|
|
+ legend('location', 'southwestoutside');
|
|
|
|
|
+ hold off;
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+%%%
|
|
|
|
|
+%%% create X matrix for given elevation angles
|
|
|
|
|
+%%%
|
|
|
|
|
+
|
|
|
|
|
+function [M x y z s t u v az el] = sphear_X_matrix(n, up, down)
|
|
|
|
|
+ eps = 0.01;
|
|
|
|
|
+ %% create arrays of angles
|
|
|
|
|
+ az = (0:360/n:360-eps)';
|
|
|
|
|
+ %% map zero elevation into spherical harmonic components
|
|
|
|
|
+ el = 0;
|
|
|
|
|
+ for i = 1:size(az)
|
|
|
|
|
+ x(i) = cos(az(i)/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ y(i) = sin(az(i)/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ z(i) = sin(el/180*pi);
|
|
|
|
|
+ s(i) = cos(az(i)/180*pi) * sin((2*el)/180*pi);
|
|
|
|
|
+ t(i) = sin(az(i)/180*pi) * sin((2*el)/180*pi);
|
|
|
|
|
+ u(i) = cos((2*az(i))/180*pi) * cos(el/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ v(i) = sin((2*az(i))/180*pi) * cos(el/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ end
|
|
|
|
|
+ %% map positive elevation measurements
|
|
|
|
|
+ el = up;
|
|
|
|
|
+ for i = 1:size(az)
|
|
|
|
|
+ x(i + 16) = cos(az(i)/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ y(i + 16) = sin(az(i)/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ z(i + 16) = sin(el/180*pi);
|
|
|
|
|
+ s(i + 16) = cos(az(i)/180*pi) * sin((2*el)/180*pi);
|
|
|
|
|
+ t(i + 16) = sin(az(i)/180*pi) * sin((2*el)/180*pi);
|
|
|
|
|
+ u(i + 16) = cos((2*az(i))/180*pi) * cos(el/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ v(i + 16) = sin((2*az(i))/180*pi) * cos(el/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ end
|
|
|
|
|
+ %% map negative elevation measurements
|
|
|
|
|
+ el = down;
|
|
|
|
|
+ for i = 1:size(az)
|
|
|
|
|
+ x(i + 32) = cos(az(i)/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ y(i + 32) = sin(az(i)/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ z(i + 32) = sin(el/180*pi);
|
|
|
|
|
+ s(i + 32) = cos(az(i)/180*pi) * sin((2*el)/180*pi);
|
|
|
|
|
+ t(i + 32) = sin(az(i)/180*pi) * sin((2*el)/180*pi);
|
|
|
|
|
+ u(i + 32) = cos((2*az(i))/180*pi) * cos(el/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ v(i + 32) = sin((2*az(i))/180*pi) * cos(el/180*pi) * cos(el/180*pi);
|
|
|
|
|
+ end
|
|
|
|
|
+ %% return big matrix of projections
|
|
|
|
|
+ M = [x' y' z' s' t' u' v'];
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+%%%
|
|
|
|
|
+%%% Fractional delay
|
|
|
|
|
+%%%
|
|
|
|
|
+%%% based on:
|
|
|
|
|
+%%% http://www.labbookpages.co.uk/audio/beamforming/fractionalDelay.html
|
|
|
|
|
+%%%
|
|
|
|
|
+%%% Also:
|
|
|
|
|
+%%% http://users.spa.aalto.fi/vpv/publications/vesan_vaitos/ch3_pt1_fir.pdf
|
|
|
|
|
+%%%
|
|
|
|
|
+%%% N = length of sinc function (should be odd)
|
|
|
|
|
+
|
|
|
|
|
+function [SINC] = sphear_delayed_sinc(N, delay)
|
|
|
|
|
+ center = floor(N / 2);
|
|
|
|
|
+ for t = (1:N)
|
|
|
|
|
+ x = (t - 1) - delay;
|
|
|
|
|
+ window = 0.54 - 0.46 * cos(2.0 * pi * (x + 0.5) / N);
|
|
|
|
|
+ SINC(t) = sinc(x - center) * window;
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+function [DSIG FIR] = sphear_delay_signal(SIG, delay, N, fft_size)
|
|
|
|
|
+ FIR = sphear_delayed_sinc(N, delay);
|
|
|
|
|
+ for m = [1:size(SIG, 1)]
|
|
|
|
|
+ for c = [1:size(SIG, 2)]
|
|
|
|
|
+ %% we add trailing zeros to let the filter ring
|
|
|
|
|
+ DSIG(m, c, :) = fftfilt(FIR, cat(1, reshape(SIG(m, c, :), 1, [])', zeros(2*size(FIR, 2), 1))', fft_size)(round(N/2):end);
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+%%%
|
|
|
|
|
+%%% Fix wobble delays in a set of measurements
|
|
|
|
|
+%%%
|
|
|
|
|
+
|
|
|
|
|
+function [FIXED] = sphear_fix_wobble(SIG, DELAY);
|
|
|
|
|
+ min_delay = min(DELAY);
|
|
|
|
|
+ DELAY = DELAY - min_delay;
|
|
|
|
|
+ max_delay = max(DELAY);
|
|
|
|
|
+ for m = [1:size(DELAY, 1)]
|
|
|
|
|
+ printf("m: %d, delay: %f\n", m, DELAY(m));
|
|
|
|
|
+ FIXED(m,:,:) = sphear_delay_signal(SIG(m,:,:), max_delay - DELAY(m), 63, 8192);
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+%%%
|
|
|
|
|
+%%% Do a basic encoder design, static A2B matrix plus FIR filters
|
|
|
|
|
+%%%
|
|
|
|
|
+
|
|
|
|
|
+function [BFF A2B FIR W X Y Z U V] = sphear_calculate_design(fref_min, fref_max)
|
|
|
|
|
+ fft_size = 8192;
|
|
|
|
|
+ N_oct = 16;
|
|
|
|
|
+ %% read in data
|
|
|
|
|
+ [AF, FS] = sphear_af_read((1:16));
|
|
|
|
|
+ %% power in reference band
|
|
|
|
|
+ [R] = sphear_signal_power(AF, FS, fref_min, fref_max, fft_size);
|
|
|
|
|
+ %% static A2B matrix
|
|
|
|
|
+ [A2B, PV, COND] = sphear_calculate_a2b(R);
|
|
|
|
|
+ %% generate a B-format signal
|
|
|
|
|
+ [BF] = sphear_af_to_bf(AF, A2B);
|
|
|
|
|
+ %% calculate power of B-format signals
|
|
|
|
|
+ [B, FC, FE] = sphear_signal_power_all(BF, FS, 300, 20000, fref_min, fref_max, N_oct, fft_size);
|
|
|
|
|
+ %% extract the shapes of wxyz[uv] filters
|
|
|
|
|
+ %% average response in cardinal directions
|
|
|
|
|
+ [W X Y Z U V] = sphear_bf_extract_filter_shapes(B, 0);
|
|
|
|
|
+ %% design FIR filters
|
|
|
|
|
+ [FIR IR MPS] = sphear_design_fir_filters(FC, 512, FS, W, X, Y, Z, U, V, 400, 18, -10);
|
|
|
|
|
+ %% re-calculate the full B-format signal with the filters
|
|
|
|
|
+ [BF, BFF] = sphear_af_to_bf(AF, A2B, FIR, fft_size);
|
|
|
|
|
+ %% get the average power in the directions of the lobes for W, XY and UV
|
|
|
|
|
+ %% and normalize the A2B matrix so that ZY and UV have the same peak amplitude
|
|
|
|
|
+ %% as W
|
|
|
|
|
+ [A2B XY_gain UV_gain] = sphear_bf_normalize_gain(A2B, BFF, FS, fref_min, fref_max, fft_size);
|
|
|
|
|
+ %% re-calculate the B-format signal
|
|
|
|
|
+ [BF, BFF] = sphear_af_to_bf(AF, A2B, FIR, fft_size);
|
|
|
|
|
+end
|
|
|
|
|
+
|