Close

Simulating Diffraction Grating

A project log for JASPER: VIS-NIR SPECTROMETER

Grating-based VIS-NIR Spectrometer: Customizable for spectral range, resolution, SNR, and detector options aided by a software design tool

tony-francisTony Francis 09/06/2025 at 11:250 Comments

In our previous blog post, we detailed the fundamental geometry of our spectrometer, where the angles of incidence (α) and diffraction (β) are derived from a single geometric parameter, Φ. To bring this theory to life, we've created an Octave script that visually simulates how a diffraction grating splits light into its constituent colors based on this very principle. This script serves as a powerful educational tool to understand the physics at play.

The core of this simulation lies in the equations we derived:

Our script uses these formulas to calculate the diffracted angles for a range of wavelengths across the visible spectrum (VIBGYOR). By setting a fixed value for and the grating's groove density, the code plots a representation of the grating, the incident light ray, and the distinct diffracted rays for each color. This approach, where a single parameter defines the geometry, is often employed in a monochromator setup where the grating rotates to select a specific wavelength.

It's important to note that while this simulation is excellent for educational purposes, our final spectrometer design employs a different strategy. Our practical design will feature a fixed angle of incidence (α), and the diffracted light will be captured by a stationary photodiode array. The choice of the detector's position is a critical design step to capture the wavelengths of interest. Therefore, while our simulator highlights the beautiful physics of a rotating grating, it is not a direct model of our final instrument.

As an example of the output of the published code, we share three example configurations demonstrating the effect of grating groove density on diffraction angle and the diffraction angle placement.

Example Configurations


Phi = 45 and grating groove density = 600 l/mm

Phi = 45 and grating groove density = 1200 l/mm

Phi = 45 and grating groove density = 1800 l/mm

We encourage you to experiment with the code by changing the value of  Φ or the Grating_Density. This will allow you to see firsthand how these parameters directly influence the angles of incidence and diffraction, and ultimately, the spectral output.

% This Octave script simulates a spectrometer, plotting the incident ray and
% the diffracted rays for the visible spectrum (VIBGYOR). The simulation
% is based on the grating equation and a non-zero angle of incidence.

% --- Input Parameters (Editable) ---
% Angle Phi in degrees. This angle defines the overall geometry of the
% spectrometer, influencing both the angle of incidence and diffraction.
% Based on the derivation at hackaday.io/project/202421-jasper-vis-nir-spectrometer/log/243174-spectrometer-design-part-3-deriving-alpha-and-beta-angles
Phi_deg = 45; % User-defined Phi angle in degrees.
Phi_rad = deg2rad(Phi_deg); % Convert to radians for calculations.

% Diffraction grating parameters.
m = 1; % Diffraction order (set to 1 as per request).

% Grating groove density (lines per mm) to grating period 'd' (in meters)
% Uncomment the desired value below to change the grating.
Grating_Density = 1800;
% d = 1/Grating_Density; % This would give d in mm
d = (1/Grating_Density) * 1e-3; % Converted to meters
% d = 3.3333e-6; % 300 grooves/mm
% d = 1.6667e-6; % 600 grooves/mm
% d = 8.3333e-7; % 1200 grooves/mm

% Define VIBGYOR wavelengths in meters
% These are approximate central wavelengths for each color band.
wavelengths = [425, 470, 500, 560, 590, 615, 680] * 1e-9;
colors = {'magenta', 'blue', 'cyan', 'green', 'yellow', '#FFA500', 'red'};
labels = {'Violet', 'Indigo', 'Blue', 'Green', 'Yellow', 'Orange', 'Red'};

% --- Calculations ---

% The angle of incidence (alpha) and diffraction (beta) are derived from
% the grating equation and the relationship phi = (alpha + beta)/2.
%
% Derived formulas from the referenced blog post:
% alpha = phi + asin( (m * lambda) / (2 * d * cos(phi)) )
% beta = phi - asin( (m * lambda) / (2 * d * cos(phi)) )

% Calculate alpha and beta for each wavelength.
alpha_rad = Phi_rad + asin((m * wavelengths) ./ (2 * d * cos(Phi_rad)));
beta_rad = Phi_rad - asin((m * wavelengths) ./ (2 * d * cos(Phi_rad)));

alpha_deg = rad2deg(alpha_rad);
beta_deg = rad2deg(beta_rad);

% --- Plotting ---

% The entire plot is rotated so the incident ray is parallel to the X-axis.
% The angle of the incident ray (alpha) is used as the rotation angle.
rot_angle = -alpha_rad(1);

% Function to rotate a point (x, y) by 'angle' radians.
function [x_rot, y_rot] = rotate_point(x, y, angle)
    x_rot = x * cos(angle) - y * sin(angle);
    y_rot = x * sin(angle) + y * cos(angle);
end

% Clear any previous plots and set up the figure.
figure(1);
clf;
hold on;
axis equal;
grid on;

% Add a subtitle for the grating groove density and Phi.
groove_density_str = ['Grating: ' num2str(Grating_Density) ' grooves/mm | \Phi: ' num2str(Phi_deg) ' deg'];

title({'Spectrometer Simulation', groove_density_str});
xlabel('X-Axis (Arbitrary Units)');
ylabel('Y-Axis (Arbitrary Units)');
xlim([-2, 2]);
ylim([-2, 2]);

% Define the origin and plot the rotated grating as a short line segment.
origin = [0, 0];
grating_length = 0.5;

% Original grating start and end points.
grating_end_x = grating_length * cos(Phi_rad - pi/2);
grating_end_y = grating_length * sin(Phi_rad - pi/2);
grating_start_x = grating_length * cos(Phi_rad + pi/2);
grating_start_y = grating_length * sin(Phi_rad + pi/2);

% Rotate the grating points.
[grating_end_x_rot, grating_end_y_rot] = rotate_point(grating_end_x, grating_end_y, rot_angle);
[grating_start_x_rot, grating_start_y_rot] = rotate_point(grating_start_x, grating_start_y, rot_angle);
line([grating_start_x_rot, grating_end_x_rot], [grating_start_y_rot, grating_end_y_rot], 'LineWidth', 3, 'Color', 'black');
text(grating_end_x_rot + 0.1, grating_end_y_rot + 0.1, 'Grating', 'FontSize', 12);

% Plot the normal to the grating. This line is at an angle of Phi_rad from the positive x-axis.
normal_length = 1.0;
normal_angle = Phi_rad;
normal_x_orig_start = -normal_length * cos(normal_angle);
normal_y_orig_start = -normal_length * sin(normal_angle);
normal_x_orig_end = normal_length * cos(normal_angle);
normal_y_orig_end = normal_length * sin(normal_angle);

[normal_x_rot_start, normal_y_rot_start] = rotate_point(normal_x_orig_start, normal_y_orig_start, rot_angle);
[normal_x_rot_end, normal_y_rot_end] = rotate_point(normal_x_orig_end, normal_y_orig_end, rot_angle);
line([normal_x_rot_start, normal_x_rot_end], [normal_y_rot_start, normal_y_rot_end], 'LineWidth', 1, 'Color', 'black', 'LineStyle', '--');
text(normal_x_rot_end + 0.1, normal_y_rot_end, 'Normal', 'FontSize', 12, 'Color', 'black');


% Plot the incident ray, which is now parallel to the X-axis.
incident_length = 1.5;
line([origin(1) - incident_length, origin(1)], [origin(2), origin(2)], 'LineWidth', 2, 'Color', 'blue', 'LineStyle', '--');
text(origin(1) - incident_length, origin(2) + 0.1, 'Incident Ray', 'FontSize', 12, 'Color', 'blue');

% Plot the diffracted rays for each VIBGYOR color.
diffracted_length = 1.5;
text_offset_length = diffracted_length + 0.1;
for i = 1:length(wavelengths)
    % Original diffracted ray endpoint.
    x_orig = origin(1) + diffracted_length * cos(beta_rad(i));
    y_orig = origin(2) + diffracted_length * sin(beta_rad(i));

    % Rotate the diffracted ray endpoint.
    [x_rot, y_rot] = rotate_point(x_orig, y_orig, rot_angle);
    line([origin(1), x_rot], [origin(2), y_rot], 'LineWidth', 2, 'Color', colors{i});

    % Original diffracted ray text endpoint.
    x_orig_text = origin(1) + text_offset_length * cos(beta_rad(i));
    y_orig_text = origin(2) + text_offset_length * sin(beta_rad(i));

    % Rotate the text endpoint.
    [x_rot_text, y_rot_text] = rotate_point(x_orig_text, y_orig_text, rot_angle);
    text(x_rot_text, y_rot_text, labels{i}, 'FontSize', 10, 'Color', colors{i});
end

% The following text labels are for angles Alpha and Beta.
% Mark and label the angles alpha and beta.
% Alpha angle is between the incident ray and the grating normal.
% text(-0.3, 0.07, '\alpha', 'FontSize', 12);
% text(-0.4, 0.05, '\alpha', 'FontSize', 12);
text(-0.3, 0.1, '\alpha', 'FontSize', 12);

% Beta angle is between the first diffracted ray and the normal.
beta_label_angle = ((rot_angle + Phi_rad) + (rot_angle + beta_rad(1))) / 2;
beta_label_x = 0.5 * cos(beta_label_angle);
beta_label_y = 0.5 * sin(beta_label_angle);
text(beta_label_x, beta_label_y, '\beta', 'FontSize', 12);

hold off;

% Print the calculated angles for reference.
fprintf('--- Calculated Angles (Degrees) ---\n');
fprintf('Phi: %f\n', Phi_deg);
for i = 1:length(wavelengths)
    fprintf('%s (%.0f nm): Alpha = %.2f, Beta = %.2f\n', ...
            labels{i}, wavelengths(i) * 1e9, rad2deg(alpha_rad(i)), rad2deg(beta_rad(i)));
end

Discussions