Chirp Signal Generation
Table of contents
Introduction
The Chirp Signal Generation Project aims at proviving basic code to generate frequency modulated continuous waves (FMCW) on the SensEdu Shield using the Arduino Giga R1. The projects provides two types of frequency modulation : sawtooth and triangular.
FMCW, also called chirp signals, are encountered in numerous fields, like radar & sonar systems, telecommunications, signal processing and more. You will find more information on the potential applications from our upcoming FMCW radar project.
Chirp signal sweeping from 100 Hz to 10 kHz
Spectrogram of chirp sweeping from 100 Hz to 10 kHz
Chirp Generation Function
Arduino does not provide any built-in chirp signal function. There are workarounds using MATLAB’s built-in chirp function but our idea was to create this signal directly in Arduino with the SensEdu library.
The generateSawtoothChirp
and generateTriangularChirp
functions both calculate the values to generate a sawtooth chirp and triangular chirp respectively and copy these values to the DAC’s buffer.
void generateSawtoothChirp(uint16_t* array)
void generateTriangularChirp(uint16_t* array)
Parameters
uint16_t* array
: A pointer to an array where the generated chirp signal will be stored.
Description
The generate chirp functions use two arrays lut_sine
and vChirp
to calculate the chirp values :
lut_sine
is a LUT containing the values of a quarter sine wave. Thex
variable defines the resolution oflut_sine
. A largerx
will result in a more detailed LUT with more values which in turn increases the precision of the chirp values which will be calculated.vChirp
is the array in which the chirp values are calculated.
The following steps describe how the function was implemented :
Step 1 : Generate the quarter-wave LUT
// Generate the quarter-wave sine LUT
for (int i = 0; i < 90 * x; i++) {
float phase_deg = (float)i * 90.0 / (90 * x); // Phase angle in degrees
float phase_rad = phase_deg * Pi / 180.0; // Phase angle to radians
lut_sine[i] = sin(phase_rad-Pi/2); // Store sine value in the LUT
}
Step 2 : Calculate the instantaneous phase of the chirp signal and wrap between 0-360 degrees
for (int i = 1; i < samples_int + 1; i++) {
float phase_rad = 2.0 * Pi * (0.5 * sK * (i - 1) / fs + START_FREQUENCY) * (i - 1) / fs; // Phase angle in radians
float phase_deg = phase_rad * 180.0 / Pi; // Phase angle to degrees
float phase_deg_wrapped = fmod(phase_deg, 360.0); // Wrap phase angle to 0-360 degrees
Step 3 : Calculate the value of the chirp using a quadrant-based approach. Scale and offset values to get 12 bit values.
if (phase_deg_wrapped <= 90) {
vChirp[i - 1] = lut_sine[(int)(phase_deg_wrapped / 90.0 * (90 * x - 1))] * 2047.5 + 2047.5;
} else if (phase_deg_wrapped <= 180) {
vChirp[i - 1] = -lut_sine[(int)((180.0 - phase_deg_wrapped) / 90.0 * (90 * x - 1))] * 2047.5 + 2047.5;
} else if (phase_deg_wrapped <= 270) {
vChirp[i - 1] = -lut_sine[(int)((phase_deg_wrapped - 180.0) / 90.0 * (90 * x - 1))] * 2047.5 + 2047.5;
} else {
vChirp[i - 1] = lut_sine[(int)((360.0 - phase_deg_wrapped) / 90.0 * (90 * x - 1))] * 2047.5 + 2047.5;
}
For the triangular modulation, the array values are mirrored on the other half.
for (int i = samples_int; i < samples_int * 2; i++) {
vChirp[i] = vChirp[samples_int * 2 - i - 1]; // Mirror the chirp signal
}
In this configuration, the first value of the chirp signal array is 0 (or 0 V in amplitude at DAC output). This initial value can be changed by modifying sine_lut
.
Step 4 : Copy the chirp signal’s values to the DAC buffer
// Copy the chirp signal to the DAC buffer
for (int i = 0; i < samples_int; i++) {
array[i] = (uint16_t)vChirp[i];
}
Main code
Check out the DAC section for more information on the DAC library and different DAC related functions.
The Chirp_SawtoothMod.ino
and Chirp_TriangularMod.ino
files contain the main code to generate the chirp signal, send the chirp signal to the DAC and enable the DAC.
The code contains the following user settings to adjust the chirp signal :
CHIRP_DURATION
: The period of the chirp signal in secondsSTART_FREQUENCY
: The start frequency of the chirp signal in HzEND_FREQUENCY
: The end frequency of the chirp signal in Hz
A very important variable in the main code is the sampling frequency fs
. Based on the Nyquist-Shannon sampling theorem, fs
needs to be at least double the maximum frequency (or end frequency) of the chirp signal.
Keep in mind this sampling frequency will also be the DAC’s output frequency in the SensEdu_DAC_Settings
function.
fs
needs to be at least 2*END_FREQUENCY in order for the chirp signal to be generated properly.
The samples_int
is an integer representing the amount of samples for one period of the chirp signal. This value also represents the memory size in the SENSEDU_DAC_BUFFER
and SensEdu_DAC_Settings
functions.
The values of the chirp are printed in the serial monitor.
// Print the chirp signal LUT
Serial.println("start of the Chirp LUT");
for (int i = 0 ; i < samples_int; i++) { // loop for the LUT size
Serial.print("value ");
Serial.print(i+1);
Serial.print(" of the Chirp LUT: ");
Serial.println(lut[i]);
}