How to add x % noise in a signal in MATLAB? - matlab

I am trying to add 5 % noise to a measured signal as follows (in MATLAB), but when I calculate percent of noise after addition, it is beyond +/- 5 % . Can you please tell me why this is the case ? Shouldnt it be within a +/- 5 % bound ?
noiseSigma = 0.05 * signal; % signal is my original signal
noise = noiseSigma .* randn(1, length(signal));
noisySignal = signal + noise;
percent_noise = 100*(noisySignal-signal)./signal;

randn draws from a normal distribution so it can be larger than 1.
If you want to limit your noise to 5 percent you can try the following:
noise_limit = 0.05 * signal;
addative_noise = noise_limit .* (2*rand(1, length(signal))-1);
result = signal+addative_noise;
This works because rand chooses values between zero and one. multiply it by two and subtract one, and it chooses values between -1 and 1.

Related

Inverse Fourier of a Discrete and Finite Sampling Window

I am trying to find the inverse Fourier of a discrete sampling window with N_f = 11 elements. I am expecting to see a periodic Sinc-like signal in the time-domain:
However, this is the output I get:
Why the number of observed oscillations in my output is more than the expected output?
N_f = 11; % Number of samples in the finite sampling window in Frequency domain
delta_f = 1;
f = [-N_f/2+1:delta_f:N_f/2];
wf = ones(1, N_f)/N_f; % W(f):finite sampling window in Frequency Domain
N_t =128;
wt = ifftshift(ifft(wf, N_t))*N_t; % w(t):Inverse Fourier of the window
I think one of the issues with your code is related to the location of the origin. You create an array wf with 11 ones (and normalized), then call ifft(wf,128). This pads the array with zeros to a size of 128, but the 11 ones are on the left side. You can see this by doing
fft(ifft(wf, N_t))
Thus, your window is shifted by 5 samples to the right, covering frequency bins 0 through 11, rather than -5 through 5 (or identically, 0 through 5 and 124 through 128).
The code below creates a signal with 128 samples, and fills the 11 frequency bins around the middle with 1/11. By calling ifftshift we rotate the signal such that the middle element is moved to the leftmost bin. Now bins 0 through 5 and 124 through 128 are non-zero. I then call ifft, and fftshift to move the 0 frequency back to the middle of the signal. Finally, I plot three repetitions of this signal.
N_f = 11; % Number of samples in the finite sampling window in Frequency domain
N_t = 128;
wf = zeros(1,N_t);
wf( N_t/2 - floor(N_f/2) + 1 : N_t/2 + floor(N_f/2) + 1 ) = 1 / N_f;
wt = fftshift(ifft(ifftshift(wf))) * N_t;
figure; plot(repmat(wt,1,3))
As you can see, the result is as you expected it. Note that the wt result of ifft is actually real-valued. The result of your inverse transform wasn't real-valued, you had to ignore the imaginary component to produce your plot. That's a sign that the input signal wasn't symmetric!
We can change N_f to be twice as large, yielding a result similar to yours, but with a purely real output:
N_f = 21;
N_t = 128;
wf = zeros(1,N_t);
wf( N_t/2 - floor(N_f/2) + 1 : N_t/2 + floor(N_f/2) + 1 ) = 1 / N_f;
wt = fftshift(ifft(ifftshift(wf))) * N_t;
figure; plot(repmat(wt,1,3))

How can I create n sine waves from the elements of an n-by-m matrix?

I'm writing a program on MATLAB that generates 13 waveforms of varying amplitude, duration, and frequency. Each waveform is repeated 5 times, which means I have 65 'trials' in total.
The total length of each trial = 1.5 ms. The sampling frequency = 4 kHz. I would like the wave to begin at 0.5 ms. Prior to the onset of the wave, and following its offset, I would like the amplitude to be zero (i.e. a 'flatline' prior to and following the wave).
I have created a 65x3 matrix where the columns denote the frequency ('hz'), amplitude ('a'), and duration (ms) of the 65 sine waves. Each row denotes a single wave.
I would like to use the information contained in this 65x3 matrix to generate 65 sine waves of amplitude 'a', frequency 'hz', and duration 'ms'. To be specific: each wave should be created using the parameters (hz,a,ms) specified in the nth row of the matrix. E.g. if row 1 = 100, 1, 50... this means I would like to generate a 100 Hz sine wave (amplitude = 1) lasting 50 ms.
I have attempted to construct a for loop to solve this problem. However, the loop returns a number of errors, and I'm not sure how to resolve them. I have adapted the code to the point where no errors are returned; however, my latest attempt seems to generate 65 waves of equal duration, when in fact the duration of each wave should be that which is stated in vector 'ms'.
Here is my latest, albeit newbie and still unsuccessful, attempt: (note that 'trials' represents the 65x3 matrix discussed above; mA = amplitude).
hz=trials(:,1); mA=trials(:,2); ms=trials(:,3);
trials_waves=zeros(65,500); % the max duration (= 500ms); unsure of this part?
for n = 1:size(order,1)
trials_waves = mA*sin(2*pi*hz*0:ms);
end
Apologies if the information provided is scarce. This is the first time I have asked a question on this website. I can provide more information if needed.
Thank you for your help.
Best,
H
Looks like you've got a good start, I'll try to help you get further towards your solution.
Make a Sine Wave
For starters, let's make a sine wave with variable rate, amplitude, and length.
Fs = 4e3; % sample rate of 4 kHz
Sr = 100; % example rate
Sa = 1; % amplitude
St = 10e-3; % signal duration is 10 ms
% To create a sine wave in MATLAB, I'm going to first create a vector of time,
% `t`, and then create the vector of sine wave samples.
N = St * Fs; % number of samples = duration times sample rate;
t = (1:N) * 1/Fs; % time increment is one over sample rate
% Now I can build my sine wave:
Wave = Sa * sin( 2 * pi * Sr * t );
figure; plot(t, Wave);
Note! This is barely enough time for a full wavelength, so be careful with slow rates and short time lengths.
Make many Sine Waves
To turn this into a loop, I need to index into vectors of input variables. Using my previous example:
Fs = 4e3; % sample rate of 4 kHz
Sr = [100 200 300]; % rates
Sa = [1 .8 .5]; % amplitudes
St = [10e-3 20e-3 25e-3]; % signal durations
nWaves = length(Sr);
N = max(St) * Fs; % number of samples = duration times sample rate;
t = (1:N) /Fs; % time increment is one over sample rate
% initialize the array
waves = zeros(nWaves, N);
for iWaves = 1:nWaves
% index into each variable
thisT = (1:St(iWaves) * Fs) * 1/Fs;
myWave = Sa(iWaves) * sin( 2 * pi * Sr(iWaves) * thisT );
waves(iWaves,1:length(myWave)) = myWave;
end
figure; plot(t, waves);
You still have one more piece, zero padding the front end of your signals, there's lots of ways to do it, one way would be to build the signal the way I've described and then concatenate an appropriate number of zeros to the front of your signal array. Feel free to ask a new question if you get stuck. Good luck!

fft / ifft deconvolution in Matlab

I have a time varying signal (time,amplitude) and a measured frequency sensitivity (frequency,amplitude conversion factor (Mf)).
I know that if I use the center frequency of my time signal to select the amplitude conversion factor (e.g. 0.0312) for my signal I get a max. converted amplitude value of 1.4383.
I have written some code to deconvolve the time varying signal and known sensitivity (i.e. for all frequencies).
where Pt is the output/converted amplitude and Mf is amplitude conversion factor data and fft(a) is the fft of the time varying signal (a).
I take the real part of the fft(a):
xdft = fft(a);
xdft = xdft(1:length(x)/2+1); % only retaining the positive frequencies
freq = Fs*(0:(L/2))/L;
where Fs is sampling frequency and L is length of signal.
convS = real(xdft).*Mf;
assuming Mf is magnitude = real (I don't have phase info). I also interpolate
Mf=interp1(freq_Mf,Mf_in,freq,'cubic');
so at the same interrogation points as freq.
I then reconstruct the signal in time domain using:
fftRespI=complex(real(convS),imag(xdft));
pt = ifft(fftRespI,L,'symmetric')
where I use the imaginary part of the fft(a).
The reconstructed signal shape looks correct but the amplitude of the signal is not.
If I set all values of Mf = 0.0312 for f=0..N MHz I expect a max. converted amplitude value of ~1.4383 (similar to if I use the center frequency) but I get 13.0560.
How do I calibrate the amplitude axis? i.e. how do I correctly multiply fft(a) by Mf?
Some improved understanding of the y axis of the abs(magnitude) and real FFT would help me I think...
thanks
You need to re-arrange the order of your weights Mf to match the MATLAB's order for the frequencies. Let's say you have a signal
N=10;
x=randn(1,N);
y=fft(x);
The order of the frequencies in the output y is
[0:1:floor(N/2)-1,floor(-N/2):1:-1] = [0 1 2 3 4 -5 -4 -3 -2 -1]
So if your weights
Mf = randn(1,N)
are defined in this order: Mf == [0 1 2 3 4 5 6 7 8 9], you will have to re-arrange
Mfshift = [Mf(1:N/2), fliplr(Mf(2:(N/2+1)))];
and then you can get your filtered output
z = ifft(fft(x).*Mshift);
which should come out real.

changing frequency using fft and ifft not using whole numbers

I know I can change frequency by whole numbers by changing the variable shift but how can I change the frequency using numbers with decimal places like .754 or 1.2345 or 67.456. If I change the variable 'shift' to a non-whole like number like 5.1 I get an error subscript indices must be either positive integers less than 2^31 or logicals from line mag2s = [mag2(shift+1:end), zeros(1,shift)];
Example Code below from question increase / decrease the frequency of a signal using fft and ifft in matlab / octave works with changing the variable shift (but it only works with whole numbers, I need it to work with decimals numbers also).
PS: I'm using octave 3.8.1 which is like matlab and I know I could change the frequency by adjusting the formula in the variable ya but ya will be a signal taken from an audio source (human speech) so it won't be an equation. The equation is just used to keep the example simple. And yes Fs is large due to the fact that signal files used are around 45 seconds long which is why I can't use resample because I get a out of memory error when used.
Here's a animated youtube video example of what I'm trying to get when I use the test equation ya= .5*sin(2*pi*1*t)+.2*cos(2*pi*3*t) and what I'm trying to get happen if I varied the variable shift from (0:0.1:5) youtu.be/pf25Gw6iS1U please keep in mind that ya will be an imported audio signal so I won't have an equation to easily adjust
clear all,clf
Fs = 2000000;% Sampling frequency
t=linspace(0,1,Fs);
%1a create signal
ya = .5*sin(2*pi*2*t);
%2a create frequency domain
ya_fft = fft(ya);
mag = abs(ya_fft);
phase = unwrap(angle(ya_fft));
ya_newifft=ifft(mag.*exp(i*phase));
% ----- changes start here ----- %
shift = 5; % shift amount
N = length(ya_fft); % number of points in the fft
mag1 = mag(2:N/2+1); % get positive freq. magnitude
phase1 = phase(2:N/2+1); % get positive freq. phases
mag2 = mag(N/2+2:end); % get negative freq. magnitude
phase2 = phase(N/2+2:end); % get negative freq. phases
% pad the positive frequency signals with 'shift' zeros on the left
% remove 'shift' components on the right
mag1s = [zeros(1,shift) , mag1(1:end-shift)];
phase1s = [zeros(1,shift) , phase1(1:end-shift)];
% pad the negative frequency signals with 'shift' zeros on the right
% remove 'shift' components on the left
mag2s = [mag2(shift+1:end), zeros(1,shift)];
phase2s = [phase2(shift+1:end), zeros(1,shift) ];
% recreate the frequency spectrum after the shift
% DC +ve freq. -ve freq.
magS = [mag(1) , mag1s , mag2s];
phaseS = [phase(1) , phase1s , phase2s];
x = magS.*cos(phaseS); % change from polar to rectangular
y = magS.*sin(phaseS);
yafft2 = x + i*y; % store signal as complex numbers
yaifft2 = real(ifft(yafft2)); % take inverse fft
plot(t,ya,'-r',t,yaifft2,'-b'); % time signal with increased frequency
legend('Original signal (ya) ','New frequency signal (yaifft2) ')
You can do this using a fractional delay filter.
First, lets make the code ore workable by letting Matlab handle the conjugate symmetry of the FFT. Just make mag1 and phase1 go to the end . . .
mag1 = mag(2:end);
phase1 = phase(2:end);
Get rid of mag2s and phase2s completely. This simplifies lines 37 and 38 to . .
magS = [mag(1) , mag1s ];
phaseS = [phase(1) , phase1s ];
Use the symmetric option of ifft to get Matlb to handle the symmetry for you. You can then drop the forced real, too.
yaifft2 = ifft(yafft2, 'symmetric'); % take inverse fft
With that cleaned up, we can now think of the delay as a filter, e.g.
% ----- changes start here ----- %
shift = 5;
shift_b = [zeros(1, shift) 1]; % shift amount
shift_a = 1;
which can be applied as so . . .
mag1s = filter(shift_b, shift_a, mag1);
phase1s = filter(shift_b, shift_a, phase1);
In this mindset, we can use an allpass filter to make a very simple fractional delay filter
The code above gives the 'M Samples Delay' part of the circuit. You can then add on the fraction using a second cascaded allpass filter . .
shift = 5.5;
Nw = floor(shift);
shift_b = [zeros(1, Nw) 1];
shift_a = 1;
Nf = mod(shift,1);
alpha = -(Nf-1)/(Nf+1);
fract_b = [alpha 1];
fract_a = [1 alpha];
%// now filter as a cascade . . .
mag1s = filter(shift_b, shift_a, mag1);
mag1s = filter(fract_b, fract_a, mag1s);
Ok so the question as I understand it is "how do I shift my signal by a specific frequency?"
First let's define Fs which is our sample rate (ie samples per second). We collect a signal which is N samples long. Then the frequency change between samples in the Fourier domain is Fs/N. So taking your example code Fs is 2,000,000 and N is 2,000,000 so the space between each sample is 1Hz and shifting your signal 5 samples shifts it 5Hz.
Now say we want to shift our signal by 5.25Hz instead. Well if our signal was 8,000,000 samples then the spacing would be Fs/N = 0.25Hz and we would shift our signal 11 samples. So how do we get an 8,000,000 sample signal from a 2,000,000 sample signal? Just zero pad it! literally append zeros until it is 8,000,000 samples long. Why does this work? Because you are in essence multiplying your signal by a rectangular window which is equivalent to sinc function convolution in the frequency domain. This is an important point. By appending zeros you are interpolating in the frequency domain (you don't have any more frequency information about the signal you are just interpolating between the previous DTFT points).
We can do this down to any resolution you want, but eventually you'll have to deal with the fact that numbers in digital systems aren't continuous so I recommend just choosing an acceptable tolerance. Lets say we want to be within 0.01 of our desired frequency.
So lets get to actual code. Most of it doesn't change luckily.
clear all,clf
Fs = 44100; % lets pick actual audio sampling rate
tolerance = 0.01; % our frequency bin tolerance
minSignalLen = Fs / tolerance; %minimum number of samples for our tolerance
%your code does not like odd length signals so lets make sure we have an
%even signal length
if(mod(minSignalLen,2) ~=0 )
minSignalLen = minSignalLen + 1;
end
t=linspace(0,1,Fs); %our input signal is 1s long
%1a create 2Hz signal
ya = .5*sin(2*pi*2*t);
if (length(ya) < minSignalLen)
ya = [ya, zeros(1, minSignalLen - length(ya))];
end
df = Fs / length(ya); %actual frequency domain spacing;
targetFreqShift = 2.32; %lets shift it 2.32Hz
nSamplesShift = round(targetFreqShift / df);
%2a create frequency domain
ya_fft = fft(ya);
mag = abs(ya_fft);
phase = unwrap(angle(ya_fft));
ya_newifft=ifft(mag.*exp(i*phase));
% ----- changes start here ----- %
shift = nSamplesShift; % shift amount
N = length(ya_fft); % number of points in the fft
mag1 = mag(2:N/2+1); % get positive freq. magnitude
phase1 = phase(2:N/2+1); % get positive freq. phases
mag2 = mag(N/2+2:end); % get negative freq. magnitude
phase2 = phase(N/2+2:end); % get negative freq. phases
% pad the positive frequency signals with 'shift' zeros on the left
% remove 'shift' components on the right
mag1s = [zeros(1,shift) , mag1(1:end-shift)];
phase1s = [zeros(1,shift) , phase1(1:end-shift)];
% pad the negative frequency signals with 'shift' zeros on the right
% remove 'shift' components on the left
mag2s = [mag2(shift+1:end), zeros(1,shift)];
phase2s = [phase2(shift+1:end), zeros(1,shift) ];
% recreate the frequency spectrum after the shift
% DC +ve freq. -ve freq.
magS = [mag(1) , mag1s , mag2s];
phaseS = [phase(1) , phase1s , phase2s];
x = magS.*cos(phaseS); % change from polar to rectangular
y = magS.*sin(phaseS);
yafft2 = x + i*y; % store signal as complex numbers
yaifft2 = real(ifft(yafft2)); % take inverse fft
%pull out the original 1s of signal
plot(t,ya(1:length(t)),'-r',t,yaifft2(1:length(t)),'-b');
legend('Original signal (ya) ','New frequency signal (yaifft2) ')
The final signal is a little over 4Hz which is what we expect. There is some distortion visible from the interpolation, but that should be minimized with a longer signal with a smother frequency domain representation.
Now that I've gone through all of that you may be wondering if there is an easier way. Fortunately for us, there is. We can take advantage of the hilbert transform and fourier transform properties to achieve a frequency shift without ever worrying about Fs or tolerance levels or bin spacing. Namely we know that a time shift leads to a phase shift in the Fourier domain. Well time and frequency are duals so a frequency shift leads to a complex exponential multiplication in the time domain. We don't want to just do a bulk shift of all frequencies because that that will ruin our symmetry in Fourier space leading to a complex time series. So we use the hilbert transform to get the analytic signal which is composed of only the positive frequencies, shift that, and then reconstruct our time series assuming a symmetric Fourier representation.
Fs = 44100;
t=linspace(0,1,Fs);
FShift = 2.3 %shift our frequency up by 2.3Hz
%1a create signal
ya = .5*sin(2*pi*2*t);
yaHil = hilbert(ya); %get the hilbert transform
yaShiftedHil = yaHil.*exp(1i*2*pi*FShift*t);
yaShifted = real(yaShiftedHil);
figure
plot(t,ya,'-r',t,yaShifted,'-b')
legend('Original signal (ya) ','New frequency signal (yaifft2) ')
Band-limited interpolation using a windowed-Sinc interpolation kernel can be used to change sample rate by arbitrary ratios. Changing the sample rate changes the frequency content of the signal, relative to the sample rate, by the inverse ratio.

identifying phase shift between signals

I have generated three identical waves with a phase shift in each. For example:
t = 1:10800; % generate time vector
fs = 1; % sampling frequency (seconds)
A = 2; % amplitude
P = 1000; % period (seconds), the time it takes for the signal to repeat itself
f1 = 1/P; % number of cycles per second (i.e. how often the signal repeats itself every second).
y1 = A*sin(2*pi*f1*t); % signal 1
phi = 10; % phase shift
y2 = A*sin(2*pi*f1*t + phi); % signal 2
phi = 15; % phase shift
y3 = A*sin(2*pi*f1*t + phi); % signal 3
YY = [y1',y2',y3'];
plot(t,YY)
I would now like to use a method for detecting this phase shift between the waves. The point of doing this is so that I can eventually apply the method to real data and identify phase shifts between signals.
So far I have been thinking of computing the cross spectra between each wave and the first wave (i.e. without the phase shift):
for i = 1:3;
[Pxy,Freq] = cpsd(YY(:,1),YY(:,i));
coP = real(Pxy);
quadP = imag(Pxy);
phase(:,i) = atan2(coP,quadP);
end
but I'm not sure if this makes any sense.
Has anyone else done something similar to this? The desired outcome should show a phase shift at 10 and 15 for waves 2 and 3 respectively.
Any advice would be appreciated.
There are several ways that you can measure the phase shift between signals. Between your response, the comments below your response, and the other answers, you've gotten most of the options. The specific choice of technique is usually based on issues such as:
Noisy or Clean: Is there noise in your signal?
Multi-Component or Single-Component: Are there more than one type of signal within your recording (multiple tones at multiple frequencies moving in different directions)? Or, is there just a single signal, like in your sine-wave example?
Instantaneous or Averaged: Are you looking for the average phase lag across your entire recording, or are you looking to track how the phase changes throughout the recording?
Depending on your answer to these questions, you could consider the following techniques:
Cross-Correlation: Use the a command like [c,lag]=xcorr(y1,y2); to get the cross-correlation between the two signals. This works on the original time-domain signals. You look for the index where c is maximum ([maxC,I]=max(c);) and then you get your lag value in units of samples lag = lag(I);. This approach gives you the average phase lag for the entire recording. It requires that your signal of interest in the recording be stronger than anything else in your recording...in other words, it is sensitive to noise and other interference.
Frequency Domain: Here you convert your signals into the frequency domain (using fft or cpsd or whatever). Then, you'd find the bin that corresponds to the frequency that you care about and get the angle between the two signals. So, for example, if bin #18 corresponds to your signal's frequency, you'd get the phase lag in radians via phase_rad = angle(fft_y1(18)/fft_y2(18));. If your signals have a constant frequency, this is an excellent approach because it naturally rejects all noise and interference at other frequencies. You can have really strong interference at one frequency, but you can still cleanly get your signal at another frequency. This technique is not the best for signals that change frequency during the fft analysis window.
Hilbert Transform: A third technique, often overlooked, is to convert your time-domain signal into an analytic signal via the Hilbert transform: y1_h = hilbert(y1);. Once you do this, your signal is a vector of complex numbers. A vector holding a simple sine wave in the time domain will now be a vector of complex numbers whose magnitude is constant and whose phase is changing in sync with your original sine wave. This technique allows you to get the instantaneous phase lag between two signals...it's powerful: phase_rad = angle(y1_h ./ y2_h); or phase_rad = wrap(angle(y1_h) - angle(y2_h));. The major limitation to this approach is that your signal needs to be mono-component, meaning that your signal of interest must dominate your recording. Therefore, you may have to filter out any substantial interference that might exist.
For two sinusoidal signal the phase of the complex correlation coefficient gives you what you want. I can only give you an python example (using scipy) as I don't have a matlab to test it.
x1 = sin( 0.1*arange(1024) )
x2 = sin( 0.1*arange(1024) + 0.456)
x1h = hilbert(x1)
x2h = hilbert(x2)
c = inner( x1h, conj(x2h) ) / sqrt( inner(x1h,conj(x1h)) * inner(x2h,conj(x2h)) )
phase_diff = angle(c)
There is a function corrcoeff in matlab, that should work, too (The python one discard the imaginary part). I.e. c = corrcoeff(x1h,x2h) should work in matlab.
The Matlab code to find relative phase using cross-correlation:
fr = 20; % input signal freq
timeStep = 1e-4;
t = 0:timeStep:50; % time vector
y1 = sin(2*pi*t); % reference signal
ph = 0.5; % phase difference to be detected in radians
y2 = 0.9 * sin(2*pi*t + ph); % signal, the phase of which, is to be measured relative to the reference signal
[c,lag]=xcorr(y1,y2); % calc. cross-corel-n
[maxC,I]=max(c); % find max
PH = (lag(I) * timeStep) * 2 * pi; % calculated phase in radians
>> PH
PH =
0.4995
With the correct signals:
t = 1:10800; % generate time vector
fs = 1; % sampling frequency (seconds)
A = 2; % amplitude
P = 1000; % period (seconds), the time it takes for the signal to repeat itself
f1 = 1/P; % number of cycles per second (i.e. how often the signal repeats itself every second).
y1 = A*sin(2*pi*f1*t); % signal 1
phi = 10*pi/180; % phase shift in radians
y2 = A*sin(2*pi*f1*t + phi); % signal 2
phi = 15*pi/180; % phase shift in radians
y3 = A*sin(2*pi*f1*t + phi); % signal 3
The following should work:
>> acos(dot(y1,y2)/(norm(y1)*norm(y2)))
>> ans*180/pi
ans = 9.9332
>> acos(dot(y1,y3)/(norm(y1)*norm(y3)))
ans = 0.25980
>> ans*180/pi
ans = 14.885
Whether or not that's good enough for your "real" signals, only you can tell.
Here is the little modification of your code: phi = 10 is actually in degree, then in sine function, phase information is mostly expressed in radian,so you need to change deg2rad(phi) as following:
t = 1:10800; % generate time vector
fs = 1; % sampling frequency (seconds)
A = 2; % amplitude
P = 1000; % period (seconds), the time it takes for the signal to repeat itself
f1 = 1/P; % number of cycles per second (i.e. how often the signal repeats itself every second).
y1 = A*sin(2*pi*f1*t); % signal 1
phi = deg2rad(10); % phase shift
y2 = A*sin(2*pi*f1*t + phi); % signal 2
phi = deg2rad(15); % phase shift
y3 = A*sin(2*pi*f1*t + phi); % signal 3
YY = [y1',y2',y3'];
plot(t,YY)
then using frequency domain method as mentioned chipaudette
fft_y1 = fft(y1);
fft_y2 = fft(y2);
phase_rad = angle(fft_y1(1:end/2)/fft_y2(1:end/2));
phase_deg = rad2deg(angle(fft_y1(1:end/2)/fft_y2(1:end/2)));
now this will give you a phase shift estimate with error = +-0.2145
If you know the frequency and just want to find the phase, rather than use a full FFT, you might want to consider the Goertzel algorithm, which is a more efficient way to calculate the DFT for a single frequency (an FFT will calculate it for all frequencies).
For a good implementation, see: https://www.mathworks.com/matlabcentral/fileexchange/35103-generalized-goertzel-algorithm and https://asp-eurasipjournals.springeropen.com/track/pdf/10.1186/1687-6180-2012-56.pdf
If you use an AWGN signal with delay and apply your method it works, but if you are using a single tone frequency estimation will not help you. because there is no energy in any other frequency but the tone. You better use cross-correlation in the time domain for this - it will work better for a fixed delay. If you have a wideband signal you can use subbands domain and estimate the phase from that (it is better than FFT due to low cross-frequency dependencies).