Close

JASPER FTIR: Octave Code for Interference and FFT Visualization

A project log for JASPER : FTIR

Featuring a ZnSe beam splitter and a pyroelectric detector, the FTIR spectrometer ensures precise molecular analysis with reliability

tony-francisTony Francis 08/06/2025 at 18:450 Comments

As part of our ongoing efforts to make complex optical principles more accessible, we've prepared an Octave script that beautifully illustrates the concepts of signal interference and Fast Fourier Transform (FFT), which are fundamental to how FTIR spectrometers work.

You can find our latest log post, "Unveiling the Spectrum: Demystifying Interference with FFT," on Hackaday here: https://hackaday.io/project/202423-jasper-ftir/log/242500-unveiling-the-spectrum-demystifying-interference-with-fft. The post features two aspects 

  1. Uniform Intensity Sine Waves: We generate a series of sine waves, all with the same intensity, ranging from 1 Hz to 10 Hz. You'll see these individual waves plotted, followed by their combined interference pattern. Crucially, the FFT of this combined signal will show distinct peaks at each of the original frequencies, all with uniform intensity, demonstrating how FFT can decompose a complex signal into its constituent frequencies.
  2. Variable Intensity Sine Waves: In this section, we repeat the process, but this time, the sine waves have varying intensities. This simulates a more realistic scenario where different frequencies might have different strengths. Again, we plot the individual waves, their interference, and then their FFT. The FFT in this case will reveal peaks at the original frequencies, but their heights will correspond to the varying intensities, providing insight into the spectral content of the signal.

All the illustrations and plots in the Hackaday post were generated using this Octave code.

We will be setting up a git hub repository where the code will be uploaded. But for now, check the code here 

We encourage you to run this code, experiment with the parameters, and see the magic of Fourier Transform in action. Your feedback and contributions are always welcome as we continue to build JASPER FTIR together!

% --- Parameters for Sine Wave Generation ---
f_min = 1;          % Minimum frequency of sine waves (Hz)
f_max = 10;         % Maximum frequency of sine waves (Hz)
overSampRate = 100; % Oversampling rate (samples per highest frequency cycle)
nCyl = 1;           % Number of cycles for the lowest frequency (f_min) to display

% Define variable amplitudes for each sine wave
% This array should have 'f_max - f_min + 1' elements
% Example: decreasing amplitude with increasing frequency
amplitudes = [1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7, 0.65, 0.6, 0.55];
% Ensure the number of amplitudes matches the number of frequencies
if length(amplitudes) ~= (f_max - f_min + 1)
    error('The number of amplitudes must match the number of frequencies (f_max - f_min + 1).');
end

% --- Derived Parameters for Time Domain ---
% Sampling frequency must be high enough to accurately represent the highest frequency
fs = overSampRate * f_max;
% Time base: spans 'nCyl' cycles of the lowest frequency (f_min)
% Adjusted time vector to ensure signal length aligns perfectly with integer frequency bins
t = 0 : 1/fs : (nCyl * 1/f_min - 1/fs); % Ensure L = fs * duration for exact bin alignment

% --- Generate and Plot Individual Sine Waves ---
figure(1);
hold on; % Keep all plots on the same figure
all_sine_waves = zeros(length(t), f_max - f_min + 1); % Pre-allocate matrix for all sine waves
legend_entries = cell(1, f_max - f_min + 1); % Pre-allocate cell array for legend entries

% Generate a colormap for distinct colors for each sine wave
colors = jet(f_max); % 'jet' colormap provides a good range of colors

fprintf('Generating and plotting individual sine waves...\n');
for k = f_min : f_max
    current_amplitude_idx = k - f_min + 1;
    current_amplitude = amplitudes(current_amplitude_idx);

    % Generate sine wave for current frequency k with its specific amplitude. Phase is 0.
    y = current_amplitude * sin(2 * pi * k * t);

    % Store the sine wave for later summation
    all_sine_waves(:, current_amplitude_idx) = y;

    % Plot the individual sine wave with a unique color from the colormap
    plot(t, y, 'color', colors(k,:), 'DisplayName', ['f = ', num2str(k), ' Hz, Amp = ', num2str(current_amplitude)]);

    % Store legend entry
    legend_entries{current_amplitude_idx} = ['f = ', num2str(k), ' Hz (Amp: ', num2str(current_amplitude), ')'];
end

% Add title and labels for the individual waves plot
title(['Individual Sine Waves (', num2str(f_min), 'Hz to ', num2str(f_max), 'Hz) with Variable Amplitudes'],'FontSize', 14);
xlabel('Time (s)');
ylabel('Amplitude');
% Display legend outside the plot area for better visibility
legend(legend_entries, 'Location', 'eastoutside', 'FontSize', 10);
grid on; % Add a grid for better readability
hold off; % Release the hold on the figure

% --- Generate and Plot Interference Signal ---
% Sum all the individual sine waves to get the interference signal
interference_signal = sum(all_sine_waves, 2);

figure(2);
% Plot the interference signal in blue with a slightly thicker line
plot(t, interference_signal, 'b', 'LineWidth', 1.5);
title(['Interference Signal (Sum of ', num2str(f_min),'Hz to ', num2str(f_max), 'Hz Sine Waves)'],'FontSize', 14);
xlabel('Time (s)');
ylabel('Amplitude');
grid on; % Add a grid for better readability

fprintf('Done with time domain plots.\n');

% --- Fast Fourier Transform (FFT) Analysis ---

% Parameters for FFT, derived from time domain parameters
Fs_fft = fs;                 % Sampling frequency for FFT (Hz)
L = length(interference_signal); % Length of the signal
T_fft = 1/Fs_fft;            % Sampling period

% Perform FFT on the interference signal
Y = fft(interference_signal);

% --- Plotting FFT Results ---

% Figure 5: Single-Sided Amplitude Spectrum
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1); % Multiply by 2 for positive frequencies (except DC and Nyquist)

figure(3); % Renamed from figure(5) as figure 3 and 4 are removed
f_plot = Fs_fft/L*(0:(L/2));
% Changed plot to stem to show distinct frequency lines
stem(f_plot, P1, "LineWidth", 1.5);
title("Single-Sided Amplitude Spectrum of Interference Signal", "FontSize", 14);
xlabel("Frequency (Hz)");
ylabel("|P1(f)| (Amplitude)");
xlim([0 f_max + 30]); % Set x-axis limit as requested
grid on;

fprintf('Done with FFT plots.\n');

Discussions