// FREQUENCY MODULATION
// 
// Several examples including sweeps, sine modulations, 
// oscillograms, spectra and sounds
// 
// -------------------------
// Author: Federico Miyara
// Date: 2020-03-06
//       2020-10-15
//       2024-10-24

clear
close(winsid())

// Sample rate
Fs = 44100;
// Durations for fast and slow sweeps
T = 2;
TT = 10;

// Time vectors 
// Fast sweep
t = 0:1/Fs:T;
// Slow sweep
tt = 0:1/Fs:TT;

// 1) Exponential sweep

// Initial and  final frequencies inicial y
f1 = 20
f2 = 20000
K = log(f2/f1)

// Frecuency vectors
f = f1 * exp(K*t/T);
ff = f1 * exp(K*t/TT);

// Phase, obtained integrating analytically the 
// frequency
// NOTE: In a difficult case to integrate analytically,
//       it could be integrated numerically, using
//       phi1 = 2*%pi*cumsum(f)/Fs; 
//       The result is virtually identical
phi = 2*%pi*f1*T/K * (exp(K*t/T) - 1);
phii = 2*%pi*f1*TT/K * (exp(K*tt/TT) - 1);

// Sweeps
x = sin(phi);
xx = sin(phii);

// Sound playback
// Fast sewwp
playsnd(0.1*x, Fs, 1)
// Slow playback
playsnd(0.1*xx, Fs, 1)

// Sweep plots 
scf(1);
clf(1);

subplot(4,1,1)
plot(t,f)

subplot(4,1,2)
plot(t,x)
gca().data_bounds = [0, -1.2; 2, 1.2];

subplot(4,1,3)
plot(t,f)
gca().data_bounds = [0, 0; 0.4, 80];

subplot(4,1,4)
plot(t,x)
gca().data_bounds = [0, -1.2; 0.4, 1.2];

 
// 2) Sine FM

// Sample rate
Fs = 44100
// Duration
T = 20
// Carrier frequency
fo = 400
// Modulating frequency
fm = 5
// Frequency deviation
deltaf = 20
// Modulation índex
mf = deltaf/fm 

// Time vector
t = 0:1/Fs:T;

// Frequency vector
f = fo + deltaf*sin(2*%pi*fm*t);

// Frequency modulated signal
x = sin(2*%pi*fo*t - mf*cos(2*%pi*fm*t));

// Playback
playsnd(0.1*x(1:2*Fs), Fs);

// Three versions of the FFT spectrum
// Size of analysis window
N = 65536
// Frequency vector
fff = (0:(N/2 - 1))*Fs/N;

// a) Plain FFT ranging a single frame
X = fft(x(1:N));
X = abs([X(1)/N, X(2:N/2)]*2/N);
X = X/sqrt(2);

// b) Average over several frames, no time window
exec fftmean.sci;
Y = fftmean(x, Fs, N, "box");

// c) Average over several frames, Hann window
Z = fftmean(x, Fs, N, "hann");

// Sine modulation plots
scf(2);
clf(2);

subplot(3,1,1)
plot(t, f)
xlabel("t  (s)")
ylabel("f  (Hz)")
gca().data_bounds = [0, 0; 0.2, (fo + deltaf)*1.1];

subplot(3,1,2)
plot(t, x)
xlabel("t  (s)")
ylabel("x_FM")
gca().data_bounds = [0, -1.2; 0.2, 1.2];

subplot(3,1,3)
// Single, no window
plot(fff, X*sqrt(2), 'k')
// Average, no window
plot(fff, Y*sqrt(2), 'b')
// Average, Hann window
plot(fff, Z*sqrt(2), 'r')  
legend("Single, no window", "Average, no window", "Average, Hann window");
xlabel("f  (Hz)")
ylabel("X_FM")
gca().data_bounds = [0, 0; 2*fo, 0.35];
gca().grid = [0,0];

// Theoretical center band
Ao = besselj(0, mf)

// Index of carrier frequency
Nfo = round(fo/(Fs/N)) + 1

// Verification 
fff(Nfo)

// Empirical center band
// To get an approximated value of the center band partial, 
// the six closest FFT lines around the center band's peak 
// value are added energetically.
// Multiplication by sqrt(2) is because theoretical spectrum
// of FM is expressed as amplitudes and empirical spectrum is
// expressed as RMS values. 
sqrt(sum(X(Nfo-3:Nfo+3).^2))*sqrt(2)
sqrt(sum(Y(Nfo-3:Nfo+3).^2))*sqrt(2)
sqrt(sum(Z(Nfo-3:Nfo+3).^2))*sqrt(2)

besselj(1:15,mf)
