MATLAB code for Harmonic Product Spectrum - matlab

Can someone tell me how I can implement Harmonic Product Spectrum using MATLAB to find the fundamental frequency of a note in the presence of harmonics?? I know I'm supposed to downsample my signal a number of times (after performing fft of course) and then multiply them with the original signal.
Say my fft signal is "FFT1"
then the code would roughly be like
hps1 = downsample(FFT1,2);
hps2 = downsample(FFT1,3);
hps = FFT1.*hps1.*hps2;
Is this code correct??? I want to know if I've downsampled properly and since each variable has a different length multiplying them results in matrix dimension error.. I really need some real quick help as its for a project work... Really desperate....
Thanx in advance....

OK you can't do "hps = FFT1.*hps1.*hps2;" for each downsampled data, do you have different sizes ...
I did a example for you how make a very simple Harmonic Product Spectrum (HPS) using 5 harmonics decimation (downsample), I just test in sinusoidal signals, I get very near fundamental frequency in my tests.
This code only shows how to compute the main steps of the algorithm, is very likely that you will need improve it !
Source:
%[x,fs] = wavread('ederwander_IN_250Hz.wav');
CorrectFactor = 0.986;
threshold = 0.2;
%F0 start test
f = 250;
fs = 44100;
signal= 0.9*sin(2*pi*f/fs*(0:9999));
x=signal';
framed = x(1:4096);
windowed = framed .* hann(length(framed));
FFT = fft(windowed, 4096);
FFT = FFT(1 : size(FFT,1) / 2);
FFT = abs(FFT);
hps1 = downsample(FFT,1);
hps2 = downsample(FFT,2);
hps3 = downsample(FFT,3);
hps4 = downsample(FFT,4);
hps5 = downsample(FFT,5);
y = [];
for i=1:length(hps5)
Product = hps1(i) * hps2(i) * hps3(i) * hps4(i) * hps5(i);
y(i) = [Product];
end
[m,n]=findpeaks(y, 'SORTSTR', 'descend');
Maximum = n(1);
%try fix octave error
if (y(n(1)) * 0.5) > (y(n(2))) %& ( ( m(2) / m(1) ) > threshold )
Maximum = n(length(n));
end
F0 = ( (Maximum / 4096) * fs ) * CorrectFactor
plot(y)
HPS usually generates an error showing the pitch one octave up, I change a bit a code, see above :-)

Related

How to resample data input signal for a different number of samples?

I am using MATLAB R2020a on a MacOS. I have an ECG signal with a sampling frequency of 1kHz. From cycle to cycle, there are a different number of samples since the cycle lenghts are different. However, I would like to have an array which has an equal number of samples (around 800) for each cycle such that those 800 sample points are automatically fitted to the original sample points regardless of the number of original sample points. I know that the resample function allows resampling at a fraction of the frequency of the input signal, but I am not sure how this would help me achieve my aim given that I would like to resample for a fixed number of points.
I would very much appreciate any suggestions. Thanks in advance
Here is my code:
% Delimit cycles in original maximum amplitude signal using indices from
% Pan Tompkins algorithm
number_cycles = round(length(qrs_i_raw)/2);
number_samples = 900;
cycle_points_maxamp = zeros(number_cycles, length(number_samples));
x_eachcycle = zeros(number_cycles, length(number_samples));
values_x = zeros(length(number_samples), 1);
y_eachcycle = zeros(number_cycles, length(number_samples));
values_y = zeros(length(number_samples), 1);
z_eachcycle = zeros(number_cycles, length(number_samples));
values_z = zeros(length(number_samples), 1);
v_eachcycle = zeros(number_cycles, length(number_samples));
w_eachcycle = zeros(number_cycles, length(number_samples));
for currentcycle = 1:length(number_cycles)
values_maxamp = maxamp(qrs_i_raw(currentcycle):qrs_i_raw(currentcycle + 1)); % need to resample to only generate 900 samples
cycle_points_maxamp(currentcycle, 1:length(values_maxamp)) = values_maxamp;
values_z(1 + 2*tau_milli_rounded(currentcycle):end) = cycle_points_maxamp(currentcycle, 1:end - 2*tau_milli_rounded(currentcycle));
z_eachcycle(currentcycle, 1:length(values_z)) = values_z;
values_y(1 + tau_milli_rounded(currentcycle):end) = cycle_points_maxamp(currentcycle, 1:end - tau_milli_rounded(currentcycle));
y_eachcycle(currentcycle, 1:length(values_y)) = values_y;
values_x(1:end) = cycle_points_maxamp(currentcycle, 1:end);
x_eachcycle(currentcycle, 1:length(values_x)) = values_x;
values_v = ((1/sqrt(6))*(x_eachcycle(currentcycle, 1:length(values_x))) + (y_eachcycle(currentcycle, 1:length(values_y))) - 2*(z_eachcycle(currentcycle, 1:length(values_z))));
v_eachcycle(currentcycle, 1:length(values_v)) = values_v;
values_w = ((1/sqrt(2))*(x_eachcycle(currentcycle, 1:length(values_x))) - (y_eachcycle(currentcycle, 1:length(values_y))));
w_eachcycle(currentcycle, 1:length(values_w)) = values_w;
end
Here are the relevant variables:
maxamp
qrs_i_raw
cyclepointsarray
To resample a single cycle of the signal to have a length of 800 points/samples the resample() function can be used with parameters 800 and length(Random_Cycle). Here I used a sinc signal arbitrarily. The resample() function will interpolate (upsample) or decimate (downsample) according to the number of available sample points in the original signal. I would first partition your signal into separate cycles apply this type of resampling the concatenate the resampled components to generate your full resampled ECG signal.
Random_Cycle = sinc(-2*pi:0.5:2*pi);
subplot(1,2,1); stem(Random_Cycle);
title("Number of Samples: " + num2str(length(Random_Cycle)));
Resampled_Signal = resample(Random_Cycle,800,length(Random_Cycle));
subplot(1,2,2); stem(Resampled_Signal);
title("Number of Samples: " + num2str(length(Resampled_Signal)));
Ran using MATLAB R2019b

Approximation of cosh and sinh functions that give large values in MATLAB

My calculation involves cosh(x) and sinh(x) when x is around 700 - 1000 which reaches MATLAB's limit and the result is NaN. The problem in the code is elastic_restor_coeff rises when radius is small (below 5e-9 in the code). My goal is to do another integral over a radius distribution from 1e-9 to 100e-9 which is still a work in progress because I get stuck at this problem.
My work around solution right now is to approximate the real part of chi_para with a step function when threshold2 hits a value of about 300. The number 300 is obtained from using the lowest possible value of radius and look at the cut-off value from the plot. I think this approach is not good enough for actual calculation since this value changes with radius so I am looking for a better approximation method. Also, the imaginary part of chi_para is difficult to approximate since it looks like a pulse instead of a step.
Here is my code without an integration over a radius distribution.
k_B = 1.38e-23;
T = 296;
radius = [5e-9,10e-9, 20e-9, 30e-9,100e-9];
fric_coeff = 8*pi*1e-3.*radius.^3;
elastic_restor_coeff = 8*pi*1.*radius.^3;
time_const = fric_coeff/elastic_restor_coeff;
omega_ar = logspace(-6,6,60);
chi_para = zeros(1,length(omega_ar));
chi_perpen = zeros(1,length(omega_ar));
threshold = zeros(1,length(omega_ar));
threshold2 = zeros(1,length(omega_ar));
for i = 1:length(radius)
for k = 1:length(omega_ar)
omega = omega_ar(k);
fric_coeff = 8*pi*1e-3.*radius(i).^3;
elastic_restor_coeff = 8*pi*1.*radius(i).^3;
time_const = fric_coeff/elastic_restor_coeff;
G_para_func = #(t) ((cosh(2*k_B*T./elastic_restor_coeff.*exp(-t./time_const))-1).*exp(1i.*omega.*t))./(cosh(2*k_B*T./elastic_restor_coeff)-1);
G_perpen_func = #(t) ((sinh(2*k_B*T./elastic_restor_coeff.*exp(-t./time_const))).*exp(1i.*omega.*t))./(sinh(2*k_B*T./elastic_restor_coeff));
chi_para(k) = (1 + 1i*omega*integral(G_para_func, 0, inf));
chi_perpen(k) = (1 + 1i*omega*integral(G_perpen_func, 0, inf));
threshold(k) = 2*k_B*T./elastic_restor_coeff*omega;
threshold2(k) = 2*k_B*T./elastic_restor_coeff*(omega*time_const - 1);
end
figure(1);
semilogx(omega_ar,real(chi_para),omega_ar,imag(chi_para));
hold on;
figure(2);
semilogx(omega_ar,real(chi_perpen),omega_ar,imag(chi_perpen));
hold on;
end
Here is the simplified function that I would like to approximate:
where x is iterated in a loop and the maximum value of x is about 700.

Iterative quantile estimation in Matlab

I'm trying to implement an interative algorithm to estimate quantiles in data that is generated from a Monte-Carlo simulation. I want to make it iterative, because I have many iterations and variables so storing all data points and using Matlab's quantile function would take much of the memory that I actually need for the simulation.
I found some approaches based on the Robbin-Monro process, given by
The implementation with a control sequence ct = c / t where c is constant is quite straight forward. In the cited paper, they show that c = 2 * sqrt(2 * pi) gives quite good results, at least for the median. But they also propose an adaptive approach based on an estimation of the histogram. Unfortunately, I haven't figured out how to implement this adaptation yet.
I tested the implementation with a constant c for three test samples with 10.000 data points. The value c = 2 * sqrt(2 * pi) did not work well for me, but c = 100 looks quite good for the test samples. However, this selction is not very robust and failed in the actual Monte-Carlo simulation giving results wide off the mark.
probabilities = [0.1, 0.4, 0.7];
controlFactor = 100;
quantile = zeros(size(probabilities));
indicator = zeros(size(probabilities));
for index = 1:length(data)
control = controlFactor / index;
indices = (data(index) >= quantile);
indicator(indices) = probabilities(indices);
indices = (data(index) < quantile);
indicator(indices) = probabilities(indices) - 1;
quantile = quantile + control * indicator;
end
Is there a more robust solution for iterative quantile estimation or does anyone have an implementation for an adaptive approach with small memory consumption?
After trying some of the adaptive iterative approaches that I found in literature without great success (not sure, if I did it right), I came up with a solution that gives me good results for my test samples and also for the actual Monte-Carlo-Simulation.
I buffer a subset of simulation results, compute the sample quantiles and average over all subset sample quantiles in the end. This seems to work quite well and without tuning many parameters. The only parameter is the buffer size which is 100 in my case.
The results converge quite fast and increasing sample size does not improve the results dramatically. There seems to be a small but constant bias that presumably is the averaged error of the subset sample quantiles. And that is the downside of my solution. By choosing the buffer size, one fixes the achievable accuracy. Increasing the buffer size reduces this bias. In the end, it seems to be a memory and accuracy tradeoff.
% Generate data
rng('default');
data = sqrt(0.5) * randn(10000, 1) + 5 * rand(10000, 1) + 10;
% Set parameters
probabilities = 0.2;
% Compute reference sample quantiles
quantileEstimation1 = quantile(data, probabilities);
% Estimate quantiles with computing the mean over a number of subset
% sample quantiles
subsetSize = 100;
quantileSum = 0;
for index = 1:length(data) / subsetSize;
quantileSum = quantileSum + quantile(data(((index - 1) * subsetSize + 1):(index * subsetSize)), probabilities);
end
quantileEstimation2 = quantileSum / (length(data) / subsetSize);
% Estimate quantiles with iterative computation
quantileEstimation3 = zeros(size(probabilities));
indicator = zeros(size(probabilities));
controlFactor = 2 * sqrt(2 * pi);
for index = 1:length(data)
control = controlFactor / index;
indices = (data(index) >= quantileEstimation3);
indicator(indices) = probabilities(indices);
indices = (data(index) < quantileEstimation3);
indicator(indices) = probabilities(indices) - 1;
quantileEstimation3 = quantileEstimation3 + control * indicator;
end
fprintf('Reference result: %f\nSubset result: %f\nIterative result: %f\n\n', quantileEstimation1, quantileEstimation2, quantileEstimation3);

adjusting swept signal equation

When I do a spectrogram in matlab / octave I can create a swept signal that looks like the RED plot line below. But how can I create a swept signal like the BLUE line in the 1st plot using the equation below.
thanks to Daniel and David for getting me this far with the code is below
startfreq=200;
fs=44100;
endfreq=20;
dursec= 10;%duration of signal in seconds
t=(0:dursec*fs)/fs; %Time vector
alpha=log(startfreq/endfreq)/dursec;
sig = exp(-j*2*pi*startfreq/alpha*exp(-alpha*t));
sig=(sig/max(abs(sig))*.8); %normalize signal
wavwrite([sig'] ,fs,32,strcat('/tmp/del.wav')); %export file
specgram(sig,150,400);
1st plot
2nd plot
How can I fix the the equation in the variable sig to get it to look like the BLUE line in the 1st plot?
3rd plot
This question is almost an month old, so you might have figured this out by now. Here's an answer in case you are still interested.
It appears that your current model for the frequency is
freq(t) = b*exp(-alpha*t)
with
freq(0) = b = startfreq
freq(dursec) = b*exp(-alpha*dursec) = endfreq
There are two free parameters (b and alpha), and two equations. The first equation, b = startfreq, gives us b (trivially).
Solving the last equation for alpha gives
alpha = -log(endfreq/startfreq)/dursec
= log(startfreq/endfreq)/dursec
So
freq(t) = startfreq * exp(-alpha*t)
To use this as the instantaneous frequency of a frequency-swept signal,
we need the integral, which I'll call phase(t):
phase(t) = -(startfreq/alpha) * exp(-alpha*t)
The (complex) frequency-swept signal is then
sig(t) = exp(2*pi*j * phase(t))
The real part of this signal is
sig(t) = cos(2*pi*phase(t))
That explains your current code. To generate a chirp whose frequency varies like the blue curve, you need a different model for the frequency. A more general model than the one used above is
freq(t) = a + b*exp(-alpha*t)
The requirements at t=0 and t=dursec are
freq(0) = a + b = startfreq
freq(dursec) = a + b*exp(-alpha*dursec) = endfreq
That's two equation, but we now have three parameters: a, b, and alpha. I'll use the two equations to determine a and b, and leave alpha as a free parameter. Solving gives
b = (startfreq - endfreq)/(1 - exp(-alpha*dursec))
a = startfreq - b
Integrating the model gives
phase(t) = a*t - (b/alpha)*exp(-alpha*t)
alpha is an arbitrary parameter. Following the formula from the first model, I'll use:
alpha = abs(log(startfreq/endfreq))/dursec
The following is a complete script. Note that I also changed the use of exp(-j*2*pi*...) to cos(2*pi*...). The factor 0.8 is there to match your code.
startfreq = 20;
endfreq = 200;
fs = 44100;
dursec = 10; % duration of signal in seconds
t = (0:dursec*fs)/fs; % Time vector
if (startfreq == endfreq)
phase = startfreq * t;
else
alpha = abs(log(endfreq/startfreq))/dursec;
b = (startfreq - endfreq)/(1 - exp(-alpha*dursec));
a = startfreq - b;
phase = a*t - (b/alpha)*exp(-alpha*t);
endif
sig = 0.8 * cos(2*pi*phase);
wavwrite([sig'] ,fs,32,strcat('del.wav')); % export file
specgram(sig,150,400);

Shifting the FFT of a signal in Matlab

I'm trying to implement a spectral correlation function to plot with the surf function.
I think I understand the idea of the SCF as described in a paper I read, but I'm having trouble implementing my function in Matlab. I've been following these instructions:
I'm mostly having trouble shifting my pieces of the data properly. Is there an easy way to achieve step 3?
Here's what I tried in my code:
function [output] = spectral(x, N)
% This function does cyclostationary spectral analysis
% on a data set and returns some features
t = length(x);
samplesPerFrame = floor(t / N);
count = 1;
for alpha = -1:0.01:1
% Split up the samples into frames
% Have to leave some samples out if unevenly split
for i = 1:N+1
frange = ((i - 1) * samplesPerFrame + 1):(i * samplesPerFrame);
if i == N+1
break;
end
xFrame(i, :) = x(frange);
ts = [1:length(xFrame(i,:))];
shiftLeft = fft(xFrame(i, :) .* exp(-1 * 2 * pi * 1i * (alpha / 2) .* ts));
shiftRight = fft(xFrame(i, :).* exp(2 * pi * 1i * (alpha / 2) .* ts));
S(i,:) = (1 / samplesPerFrame) .* shiftLeft .* conj(shiftRight);
end
Savg(count, :) = mean(S, 1);
Ssmooth(count, :) = smooth(Savg(count,:), 'moving');
count = count + 1;
end
output = Ssmooth;
end
It looks good actually.
You may also try circshift(fft(xFrame(i, :)),[1,a]) to achieve shiftRight, and circshift(fft(xFrame(i, :)),[1,-a]) to get shiftLeft. Please note here a is integer, indicates the elements in xFrame(i, :) that you wish to move, and corresponds to Fs*a in frequency domain where Fs is your sampling rate.
The method of spectral correlation estimation you are attempting is something I refer to as the Time-Smoothing Method of spectral correlation estimation, or the TSM. The code you posted cannot provide the correct answer except in some trivial cases such as alpha = 0. The reason is that you need to adjust the cyclic periodogram for each frame by a complex phase factor to compensate for the fact that each data block is a delayed version of the one preceding it.
If you replace the line
S(i,:) = (1 / samplesPerFrame) .* shiftLeft .* conj(shiftRight);
with the two lines
S(i,:) = (1 / samplesPerFrame) .* shiftLeft .* conj(shiftRight);
S(i, :) = S(i, :) * exp(-1i * 2 * pi * alpha * i * samplesPerFrame);
you'll be able to estimate the SCF. I confirmed this by applying your original code and the modified code to a BPSK signal with bit rate (normalized) of 1/10. In this case, one of your alpha values in the loop over alpha will exactly coincide with the true cycle frequency of 1/10. Only the modified code gives the correct SCF for the bit-rate cycle frequency.
Please see my blog cyclostationary.wordpress.com for more detail and examples. In particular, I have a post on the TSM at
http://cyclostationary.blog/2015/12/18/csp-estimators-the-time-smoothing-method. (Corrected this link 5/2/17.)