├── .gitignore ├── Makefile ├── README.md ├── scripts ├── plot_processed_data.m ├── plot_processed_data.sci ├── plot_simulation_data.m └── plot_simulation_data.sci └── src ├── algorithms.c ├── algorithms.h ├── common_functions.c ├── common_functions.h ├── common_types.h ├── file_io.c ├── file_io.h ├── filters.c ├── filters.h ├── sar_simulator.c ├── sar_simulator.h ├── waveforms.c └── waveforms.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | sar_simulator 3 | *.dat 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-O3 -fomit-frame-pointer -fstrict-aliasing -ffast-math -msse2 -march=native -mcmodel=medium -g 2 | LDFLAGS=-lfftw3 -lm 3 | 4 | all: sar_simulator 5 | 6 | sar_simulator: waveforms.o file_io.o algorithms.o sar_simulator.o filters.o common_functions.o 7 | ${CC} $(wildcard obj/*.o) $(LDFLAGS) -o $@ 8 | 9 | %.o: **/%.c 10 | ${CC} ${CFLAGS} -c $< -o obj/$@ 11 | 12 | clean: 13 | rm obj/* 14 | rm sar_simulator 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Contact 2 | You may contact me (Alexander Rajula) at alexander@rajula.se or superrajula@gmail.com 3 | 4 | If you have any questions about the code, or have found it useful, don't hesitate to e-mail me. 5 | 6 | # Copyright 7 | The code and all files within this folder is supplied "as-is". 8 | You are allowed to make copies of it and modify it to your wishes as long as you leave a note in 9 | your code (or other documentation) about its original author. 10 | 11 | # Introduction 12 | The code in this folder is a synthetic aperture radar (SAR) simulator. 13 | It was written for my thesis called "SAR imaging with a hand-held UWB radar system" at 14 | Blekinge Institute of Technology in Karlskrona, Sweden, 15 | and served the purpose of simulating SAR images, as well as processing real radar images. 16 | Its main purpose was to validate the implementation of common SAR algorithms: 17 | 18 | * Chirp waveform generation 19 | * Matched chirp waveform generation 20 | * Pulse-compression 21 | * Radar imaging 22 | * Radio frequency interference generation/simulation 23 | * Radio frequency interference suppression 24 | * SAR imaging (focusing) with global backprojection (GBP) 25 | * SAR 2D FFT generation 26 | 27 | It is my hope that the code may be of use to somebody in the future, either as a stepping stone 28 | for continued work, or as something to study to learn how SAR systems behave. 29 | 30 | # Known bugs 31 | For some frequency and TBP combinations, the simulator crashes in FFTW, and I don't know why. 32 | If anyone finds the reason and can fix it, please e-mail me a patch. 33 | 34 | # Required libraries 35 | To compile and link the code you will need the FFTW, math and complex libraries. 36 | The fftw library is located at www.fftw.org 37 | The math and complex libraries are usually included in Linux. 38 | The complex library is needed since the simulator operates on complex numbers. 39 | 40 | # Compilation 41 | Before you compile, check the flags set to the C compiler in Makefile 42 | Some of the flags may not be applicable to your processor or compiler version. 43 | -------------------------------------------------------------------------------- /scripts/plot_processed_data.m: -------------------------------------------------------------------------------- 1 | function plot_processed_data() 2 | 3 | distance = 10; 4 | chirp_length = 5; 5 | 6 | rows = 512; 7 | cols = 317; 8 | 9 | cradar_image = dlmread('radar_image.dat', '\t'); 10 | csar_image = dlmread('sar_image.dat', '\t'); 11 | csar_fft = dlmread('sar_fft.dat', '\t'); 12 | 13 | c = [1:2:cols-2]; 14 | 15 | radar_image = cradar_image(:, c); 16 | sar_image = csar_image(:, c); 17 | sar_fft = csar_fft(:, c); 18 | 19 | distance_step = distance/chirp_length; 20 | end_row_distance = distance_step*rows; 21 | end_col_distance = distance_step*cols; 22 | 23 | x = [0:distance_step:end_row_distance-distance_step]; 24 | y = [0:distance_step:end_col_distance-distance_step]; 25 | 26 | figure('Name', 'Radar image', 'NumberTitle', 'Off'); 27 | imagesc(x,y,abs(radar_image)) 28 | title('Radar image') 29 | xlabel('Range (m)') 30 | ylabel('Azimuth (m)') 31 | 32 | figure('Name', 'SAR image', 'NumberTitle', 'Off'); 33 | imagesc(x,y,abs(sar_image)) 34 | title('SAR image') 35 | xlabel('Range (m)') 36 | ylabel('Azimuth (m)') 37 | 38 | figure('Name', 'SAR FFT image', 'NumberTitle', 'Off'); 39 | imagesc(x,y,abs(sar_fft)) 40 | title('Radar image') 41 | xlabel('Range (m)') 42 | ylabel('Azimuth (m)') -------------------------------------------------------------------------------- /scripts/plot_processed_data.sci: -------------------------------------------------------------------------------- 1 | function plot_processed_data() 2 | 3 | //stacksize('max'); 4 | 5 | //dimensions = read("dimensions.dat", 4, 1); 6 | //radar_rows = dimensions(2); 7 | //radar_cols = dimensions(3); 8 | 9 | radar_rows = 512; 10 | radar_cols = 315; 11 | 12 | printf("Radar Rows: %i Cols: %i\n", radar_rows, radar_cols); 13 | 14 | processed_image_data = read("radar_image.dat", radar_cols, radar_rows); 15 | //processed_compressed_data = read("pulse_compressed_image.dat", radar_cols, radar_rows); 16 | processed_sar_data = read("sar_image.dat", radar_cols, radar_rows); 17 | processed_sar_fft = read("sar_fft.dat", radar_cols, radar_rows); 18 | 19 | da = gda(); 20 | da.x_label.text = "Range (m)"; 21 | da.y_label.text = "Crossrange (m)"; 22 | figure("Figure_name", "Radar image"); 23 | //plot3d([0:distance_step:end_row_distance-distance_step], [0:distance_step:end_col_distance-distance_step], 10000*abs(processed_image_data)); 24 | mesh(10000*abs(processed_image_data)); 25 | 26 | //da = gda(); 27 | //da.x_label.text = "Range (m)"; 28 | //da.y_label.text = "Crossrange (m)"; 29 | //figure("Figure_name", "Pulse-compressed Radar image"); 30 | //plot3d([0:distance_step:end_row_distance-distance_step], [0:distance_step:end_col_distance-distance_step], 10000*abs(processed_compressed_data)); 31 | //mesh(10000*abs(processed_compressed_data)); 32 | 33 | da = gda(); 34 | da.x_label.text = "Range (m)"; 35 | da.y_label.text = "Crossrange (m)"; 36 | figure("Figure_name", "SAR image"); 37 | //plot3d([0:distance_step:end_row_distance-distance_step], [0:distance_step:end_col_distance-distance_step], 10000*abs(processed_sar_data)); 38 | mesh(10000*abs(processed_sar_data)); 39 | 40 | da = gda(); 41 | da.x_label.text = "Range frequency"; 42 | da.y_label.text = "Crossrange frequency"; 43 | figure("Figure_name", "SAR image FFT"); 44 | mesh(abs(processed_sar_fft')); 45 | 46 | endfunction 47 | -------------------------------------------------------------------------------- /scripts/plot_simulation_data.m: -------------------------------------------------------------------------------- 1 | function plot_simulation_data() 2 | delimiter = '\t'; 3 | 4 | % Open up and parse dimensions file. 5 | fp = fopen('../output/dimensions.dat'); 6 | 7 | if(fp == -1) 8 | disp(strcat('Could not open dimensions.dat for reading - exiting.')); 9 | return; 10 | end 11 | 12 | while( ~feof(fp) ) 13 | % Read file descriptor and size. 14 | name = fgetl(fp); 15 | rows = str2num(fgetl(fp)); 16 | cols = str2num(fgetl(fp)); 17 | 18 | % Ignore empty matrices. 19 | if(rows < 1) 20 | continue; 21 | end 22 | 23 | if(cols < 1) 24 | continue; 25 | end 26 | 27 | % Read file as a matrix. 28 | fname = strcat("../output/"+name, '.dat'); 29 | rimg = dlmread(fname, delimiter); 30 | 31 | if(cols == 1) 32 | img = zeros(1,rows); 33 | 34 | rvect = 1:rows; 35 | 36 | img(1,rvect) = rimg(2*rvect-1)+rimg(2*rvect)*1i; 37 | 38 | figure('Name', name, 'NumberTitle', 'Off'); 39 | title(name); 40 | plot(abs(img)); 41 | xlabel('Time samples'); 42 | ylabel('Amplitude'); 43 | 44 | elseif(cols >= 2) 45 | r = rows; 46 | c = cols; 47 | cols = r; 48 | rows = c; 49 | img = zeros(rows, cols); 50 | 51 | cvect = 1:cols; 52 | rvect = 1:rows; 53 | 54 | img(rvect, cvect) = rimg(rvect,2*cvect-1)+rimg(rvect,2*cvect)*1i; 55 | 56 | figure('Name', name, 'NumberTitle', 'Off'); 57 | imagesc(cvect,rvect,abs(img)); 58 | title(name); 59 | xlabel('Range (m)'); 60 | ylabel('Azimuth (m)'); 61 | 62 | end 63 | 64 | end 65 | 66 | fclose(fp); -------------------------------------------------------------------------------- /scripts/plot_simulation_data.sci: -------------------------------------------------------------------------------- 1 | function [processed_chirp_data, processed_matched_chirp, processed_pulse_compressed, processed_scene_data, processed_undistorted_image_data, processed_unfiltered_image_data, processed_image_data, processed_compressed_data, processed_sar_data, processed_sar_fft] = plot_simulation_data() 2 | 3 | stacksize('max'); 4 | 5 | data_type = input("Is the data real or complex valued (r/c)? ", "string"); 6 | 7 | dimensions = read("dimensions.dat", 4, 1); 8 | chirp_length = dimensions(1); 9 | radar_rows = dimensions(2); 10 | radar_cols = dimensions(3); 11 | signal_distance = dimensions(4); 12 | 13 | distance_step = signal_distance/chirp_length; 14 | end_row_distance = distance_step*radar_rows; 15 | end_col_distance = distance_step*radar_cols; 16 | 17 | printf("Chirp length: %i\n", chirp_length); 18 | printf("Radar Rows: %i Cols: %i\n", radar_rows, radar_cols); 19 | 20 | if data_type == "c" 21 | chirp_data = read("chirp.dat", chirp_length, 2); 22 | matched_chirp_data = read("matched.dat", chirp_length, 2); 23 | pulse_compressed_data = read("compressed.dat", chirp_length, 2); 24 | scene_with_waveform_data = read("scene_with_waveform.dat", radar_cols, 2*radar_rows); 25 | undistorted_radar_image_data = read("undistorted_radar_image.dat", radar_cols, 2*radar_rows); 26 | unfiltered_radar_image_data = read("unfiltered_radar_image.dat", radar_cols, 2*radar_rows); 27 | radar_image_data = read("radar_image.dat", radar_cols, 2*radar_rows); 28 | compressed_image_data = read("pulse_compressed_image.dat", radar_cols, 2*radar_rows); 29 | sar_image_data = read("sar_image.dat", radar_cols, 2*radar_rows); 30 | sar_fft_data = read("sar_fft.dat", radar_cols, 2*radar_rows); 31 | 32 | chirp_fft = read("chirpfft.dat", chirp_length, 2); 33 | matched_fft = read("matchedfft.dat", chirp_length, 2); 34 | 35 | processed_chirp_data = chirp_data(:,1)+%i*chirp_data(:,2); 36 | processed_matched_chirp = matched_chirp_data(:,1)+%i*matched_chirp_data(:,2); 37 | processed_pulse_compressed = pulse_compressed_data(:,1)+%i*pulse_compressed_data(:,2); 38 | processed_scene_data = zeros(radar_rows, radar_cols); 39 | processed_undistorted_image_data = zeros(radar_rows, radar_cols); 40 | processed_unfiltered_image_data = zeros(radar_rows, radar_cols); 41 | processed_image_data = zeros(radar_rows, radar_cols); 42 | processed_compressed_data = zeros(radar_rows, radar_cols); 43 | processed_sar_data = zeros(radar_rows, radar_cols); 44 | processed_sar_fft = zeros(radar_rows, radar_cols); 45 | 46 | complex_chirp_fft = chirp_fft(:,1)+%i*chirp_fft(:,2); 47 | complex_matched_fft = matched_fft(:,1)+%i*matched_fft(:,2); 48 | 49 | for col = [1:radar_cols] 50 | for row = [1:radar_rows] 51 | processed_scene_data(row,col) = scene_with_waveform_data(col, 2*row-1) + %i*scene_with_waveform_data(col, 2*row); 52 | processed_undistorted_image_data(row,col) = undistorted_radar_image_data(col,2*row-1) + %i*undistorted_radar_image_data(col, 2*row); 53 | processed_unfiltered_image_data(row,col) = unfiltered_radar_image_data(col,2*row-1) + %i*unfiltered_radar_image_data(col, 2*row); 54 | processed_image_data(row,col) = radar_image_data(col,2*row-1) + %i*radar_image_data(col, 2*row); 55 | processed_compressed_data(row,col) = compressed_image_data(col, 2*row-1) + %i*compressed_image_data(col, 2*row); 56 | processed_sar_data(row,col) = sar_image_data(col,2*row-1) + %i*radar_image_data(col, 2*row); 57 | processed_sar_fft(row,col) = sar_fft_data(col, 2*row-1) + %i*sar_fft_data(col, 2*row); 58 | end 59 | end 60 | elseif data_type == "r" 61 | processed_chirp_data = read("chirp.dat", chirp_length, 1); 62 | processed_matched_chirp = read("matched.dat", chirp_length, 1); 63 | processed_pulse_compressed = read("compressed.dat", chirp_length, 1); 64 | processed_scene_data = read("scene_with_waveform.dat", radar_cols, radar_rows); 65 | processed_undistorted_image_data = read("undistorted_radar_image.dat", radar_cols, radar_rows); 66 | processed_unfiltered_image_data = read("unfiltered_radar_image.dat", radar_cols, radar_rows); 67 | processed_image_data = read("radar_image.dat", radar_cols, radar_rows); 68 | processed_compressed_data = read("pulse_compressed_image.dat", radar_cols, radar_rows); 69 | processed_sar_data = read("sar_image.dat", radar_cols, radar_rows); 70 | processed_sar_fft = read("sar_fft.dat", radar_cols, radar_rows); 71 | 72 | complex_chirp_fft = read("chirpfft.dat", chirp_length,1); 73 | complex_matched_fft = read("matchedfft.dat", chirp_length, 1); 74 | else 75 | printf("Invalid data type."); 76 | return 77 | end 78 | 79 | da = gda(); 80 | da.x_label.text = "Time"; 81 | da.y_label.text = "Amplitude"; 82 | figure("Figure_name", "Chirp signal"); 83 | plot(processed_chirp_data); 84 | 85 | da = gda(); 86 | da.x_label.text = "Frequency"; 87 | da.y_label.text = "Amplitude"; 88 | figure("Figure_name", "Chirp FFT"); 89 | plot(abs(complex_chirp_fft)); 90 | 91 | da = gda(); 92 | da.x_label.text = "Time"; 93 | da.y_label.text = "Amplitude"; 94 | figure("Figure_name", "Matched Chirp signal"); 95 | plot(processed_matched_chirp); 96 | 97 | da = gda(); 98 | da.x_label.text = "Frequency"; 99 | da.y_label.text = "Amplitude"; 100 | figure("Figure_name", "Matched Chirp FFT"); 101 | plot(abs(complex_matched_fft)); 102 | 103 | da = gda(); 104 | da.x_label.text = "Time"; 105 | da.y_label.text = "Amplitude"; 106 | figure("Figure_name", "Pulse-compressed signal"); 107 | plot(abs(processed_pulse_compressed)); 108 | 109 | [x,y] = meshgrid([0:distance_step:end_row_distance-distance_step], [0:distance_step:end_col_distance-distance_step]); 110 | 111 | da = gda(); 112 | da.y_label.text = "Range (m)"; 113 | da.x_label.text = "Crossrange (m)"; 114 | figure("Figure_name", "Scene with waveform"); 115 | //plot3d([0:distance_step:end_row_distance-distance_step], [0:distance_step:end_col_distance-distance_step], 10000*abs(processed_scene_data)); 116 | mesh(x,y,10000*abs(processed_scene_data)); 117 | // contour([0:distance_step:end_row_distance-distance_step],[0:distance_step:end_col_distance-distance_step],abs(processed_scene_data),10); 118 | 119 | da = gda(); 120 | da.y_label.text = "Range (m)"; 121 | da.x_label.text = "Crossrange (m)"; 122 | figure("Figure_name", "Undistorted radar image"); 123 | //plot3d([0:distance_step:end_row_distance-distance_step], [0:distance_step:end_col_distance-distance_step], 10000*abs(processed_image_data)); 124 | mesh(x,y,10000*abs(processed_undistorted_image_data)); 125 | // contour([0:distance_step:end_row_distance-distance_step],[0:distance_step:end_col_distance-distance_step],abs(processed_undistorted_image_data),10); 126 | 127 | da = gda(); 128 | da.y_label.text = "Range (m)"; 129 | da.x_label.text = "Crossrange (m)"; 130 | figure("Figure_name", "Unfiltered radar image"); 131 | //plot3d([0:distance_step:end_row_distance-distance_step], [0:distance_step:end_col_distance-distance_step], 10000*abs(processed_image_data)); 132 | mesh(x,y,10000*abs(processed_unfiltered_image_data)); 133 | 134 | da = gda(); 135 | da.y_label.text = "Range (m)"; 136 | da.x_label.text = "Crossrange (m)"; 137 | figure("Figure_name", "Radar image"); 138 | //plot3d([0:distance_step:end_row_distance-distance_step], [0:distance_step:end_col_distance-distance_step], 10000*abs(processed_image_data)); 139 | mesh(x,y,10000*abs(processed_image_data)); 140 | 141 | da = gda(); 142 | da.y_label.text = "Range (m)"; 143 | da.x_label.text = "Crossrange (m)"; 144 | figure("Figure_name", "Pulse-compressed Radar image"); 145 | //plot3d([0:distance_step:end_row_distance-distance_step], [0:distance_step:end_col_distance-distance_step], 10000*abs(processed_compressed_data)); 146 | mesh(x,y,10000*abs(processed_compressed_data)); 147 | 148 | da = gda(); 149 | da.y_label.text = "Range (m)"; 150 | da.x_label.text = "Crossrange (m)"; 151 | figure("Figure_name", "SAR image"); 152 | //plot3d([0:distance_step:end_row_distance-distance_step], [0:distance_step:end_col_distance-distance_step], 10000*abs(processed_sar_data)); 153 | mesh(x,y,10000*abs(processed_sar_data)); 154 | 155 | da = gda(); 156 | da.y_label.text = "Range frequency"; 157 | da.x_label.text = "Crossrange frequency"; 158 | figure("Figure_name", "SAR image FFT"); 159 | mesh(abs(processed_sar_fft')); 160 | 161 | endfunction 162 | -------------------------------------------------------------------------------- /src/algorithms.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Alexander Rajula 3 | * Contact: alexander@rajula.org 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "common_functions.h" 13 | #include "algorithms.h" 14 | #include "filters.h" 15 | 16 | /* GBP is the main SAR image formation (or de-skewness algorithm as I like to call it). 17 | * It focuses the hyperbolas which are a result of the stripmap radar scan into point-like objects with sidelobes. 18 | * In general, it does a superb job, if the input data is "nice enough". 19 | * It handles arbitrary scene sizes, and is thus only restricted by computer memory and processing time. 20 | * The downside of GBP is its processing time, which is in the order of O(N^3). 21 | */ 22 | void gbp(matrix* radar_image, matrix* sar_image, radar_variables* variables){ 23 | sar_image->rows = radar_image->rows; 24 | sar_image->cols = radar_image->cols; 25 | sar_image->data = malloc(sar_image->rows*sar_image->cols*sizeof(double complex)); 26 | 27 | unsigned int range_index; 28 | unsigned int cols = sar_image->cols; 29 | unsigned int rows = sar_image->rows; 30 | 31 | printf("Running GBP.\n"); 32 | 33 | for(unsigned int j = 0; j < cols; j++){ 34 | printf("Pass %i of %i.\n", j, cols); 35 | 36 | for(unsigned int k = 0; k < rows; k++){ 37 | for(unsigned int l = 0; l < cols; l++){ 38 | range_index = sqrt((l-j)*(l-j)+k*k); 39 | if(range_index < rows){ 40 | sar_image->data[j*rows+k] += radar_image->data[l*rows+range_index]; 41 | } 42 | } 43 | } 44 | } 45 | 46 | printf("GBP done.\n"); 47 | } 48 | 49 | /* To evaluate the quality of a simulated SAR image, one often looks at the spectrum of the GBP image. 50 | * For a point object in the GBP image, the 2D FFT should have a "fan" shape. */ 51 | void gbp_fft(matrix* sar_image, matrix* sar_image_fft, radar_variables* variables){ 52 | unsigned int rows = sar_image->rows; 53 | unsigned int cols = sar_image->cols; 54 | 55 | sar_image_fft->rows = rows; 56 | sar_image_fft->cols = cols; 57 | sar_image_fft->data = malloc(rows*cols*sizeof(double complex)); 58 | 59 | complex double* sar_img_shifted = malloc(rows*cols*sizeof(double complex)); 60 | 61 | // This shifts the frequencies of the 2D FFT so that zero frequency is in the middle of the image. 62 | // Also pre-normalizes the data since the 2D FFT scales by a factor of rows*cols. 63 | for(unsigned int i = 0; i < cols; i++){ 64 | for(unsigned int j = 0; j < rows; j++){ 65 | sar_img_shifted[rows*i+j] = sar_image->data[rows*i+j]*pow(-1,i+j)/(cols*rows); 66 | } 67 | } 68 | 69 | fftw_plan fft = fftw_plan_dft_2d(cols, rows, sar_img_shifted, sar_image_fft->data, FFTW_FORWARD, FFTW_ESTIMATE); 70 | fftw_execute(fft); 71 | fftw_destroy_plan(fft); 72 | 73 | free(sar_img_shifted); 74 | } 75 | 76 | /* This radar imaging algorithm is designed by me, and simulates scanning over a scene. 77 | * It takes antenna azimuth beamwidth and antenna elevation into account, and calculates how many azimuth bins the antenna sees from each azimuth position. 78 | * All the data within the antenna's field of view is compressed into a single column, for each column, taking distance into account. 79 | * This is what generates the familiar hyperbola in the raw radar image. 80 | */ 81 | void radar_imager(matrix* scene, matrix* radar_image, matrix* chirp, radar_variables* variables){ 82 | printf("Which algorithm should I use to simulate the scan?\n"); 83 | printf("1) Radar imaging algorithm\n"); 84 | printf("2) Coherent standard algorithm, time delay method\n"); 85 | printf("3) Coherent standard algorithm, phase shift method\n"); 86 | 87 | unsigned int algorithm = 0; 88 | scanf("%u", &algorithm); 89 | 90 | unsigned int rows = scene->rows; 91 | unsigned int cols = scene->cols; 92 | 93 | radar_image->rows = rows; 94 | radar_image->cols = cols; 95 | radar_image->data = malloc(rows*cols*sizeof(complex double)); 96 | 97 | if(algorithm == 1){ 98 | printf("Using Radar imaging algorithm.\n"); 99 | 100 | printf("Enter antenna azimuth beamwidth in radians: "); 101 | scanf("%f", &variables->beamwidth); 102 | 103 | printf("Enter SAR platform height: "); 104 | scanf("%d", &variables->altitude); 105 | 106 | unsigned int i,j,k; 107 | int beam_value; 108 | double azimuth_coverage = round(variables->altitude*tan(0.5*variables->beamwidth)); 109 | unsigned int beamcrossrange = variables->chirp_samples*azimuth_coverage/variables->signal_distance; 110 | 111 | printf("Antenna beam covers %f meters, %i columns.\n", azimuth_coverage, beamcrossrange); 112 | for(i = 0; i < cols; i++){ 113 | printf("Executing RIA, pass %i of %i.\n", i+1, cols); 114 | 115 | for(j = 0; j < 2*beamcrossrange; j++){ 116 | beam_value = j-beamcrossrange; 117 | for(k = 0; k < rows; k++){ 118 | if(i+beam_value < cols){ 119 | unsigned int dist = sqrt(pow(beam_value,2)+pow(k, 2)); 120 | if(dist < rows){ 121 | if(i+beam_value >= 0){ 122 | radar_image->data[i*rows+dist] += scene->data[(i+beam_value)*rows+k]; 123 | } 124 | } 125 | } 126 | } 127 | } 128 | } 129 | 130 | printf("Radar imaging algorithm done.\n"); 131 | } 132 | else if(algorithm == 2){ 133 | printf("Using coherent standard algorithm, method one.\n"); 134 | 135 | for(unsigned int i = 0; i < cols; i++){ 136 | unsigned int dist = 0; 137 | 138 | if(i < cols/2){ 139 | dist = sqrt(pow(cols/2-i,2)+pow(rows/2-variables->chirp_samples/2,2)); 140 | } 141 | else{ 142 | dist = sqrt(pow(i-cols/2,2)+pow(rows/2-variables->chirp_samples/2,2)); 143 | } 144 | 145 | if(dist+variables->chirp_samples < rows){ 146 | memcpy(&radar_image->data[i*rows+dist], chirp->data, chirp->rows*sizeof(complex double)); 147 | 148 | for(unsigned int j = 0; j < chirp->rows; j++){ 149 | radar_image->data[i*rows+dist+j] /= pow(dist,4); 150 | } 151 | } 152 | } 153 | printf("Standard algorithm done.\n"); 154 | } 155 | else{ 156 | printf("Using coherent standard algorithm, method two.\n"); 157 | 158 | memset(radar_image->data, 0, rows*cols*sizeof(complex double)); 159 | 160 | // Insert sent waveform. 161 | for(unsigned int i = 0; i < cols; i++){ 162 | memcpy(&radar_image->data[i*rows], chirp->data, chirp->rows*sizeof(complex double)); 163 | } 164 | 165 | double sample_distance = variables->signal_distance/variables->chirp_samples; 166 | 167 | double* freq = malloc(rows*sizeof(double)); 168 | double freq_delta = 5*variables->bandwidth/rows; 169 | double freq_inc = 0; 170 | 171 | for(unsigned int i = 0; i < rows; i++){ 172 | freq[i] = freq_inc; 173 | freq_inc += freq_delta; 174 | } 175 | 176 | // Create the hyperbola. 177 | for(unsigned int i = 0; i < cols; i++){ 178 | unsigned int dist = 0; 179 | if(i < cols/2) 180 | dist = sqrt(pow(cols/2-i,2)+pow(rows/2-chirp->rows/2,2)); 181 | else 182 | dist = sqrt(pow(i-cols/2,2)+pow(rows/2-chirp->rows/2,2)); 183 | 184 | fftw_plan fft = fftw_plan_dft_1d(rows, &radar_image->data[i*rows], &radar_image->data[i*rows], FFTW_FORWARD, FFTW_ESTIMATE); 185 | fftw_execute(fft); 186 | fftw_destroy_plan(fft); 187 | 188 | double in_transit_time = dist*sample_distance/C; 189 | for(unsigned int j = 0; j < rows; j++){ 190 | // The division of "rows" here is to normalize the FFT->IFFT operation, since the FFTW library doesn't re-normalize by default. 191 | radar_image->data[i*rows+j] *= cexp(-_Complex_I*2*PI*freq[j]*in_transit_time)/(rows*pow(dist,4)); 192 | } 193 | 194 | fftw_plan ifft = fftw_plan_dft_1d(rows, &radar_image->data[i*rows], &radar_image->data[i*rows], FFTW_BACKWARD, FFTW_ESTIMATE); 195 | fftw_execute(ifft); 196 | fftw_destroy_plan(ifft); 197 | 198 | } 199 | 200 | free(freq); 201 | 202 | printf("Standard algorithm done.\n"); 203 | } 204 | 205 | } 206 | 207 | // This just copies the waveform into the center of the scene, to simulate an object at the center. 208 | void insert_waveform_in_scene(matrix* chirp, matrix* scene, radar_variables* variables){ 209 | printf("The target will be placed in the middle of the simulated area.\n"); 210 | 211 | printf("Enter area azimuth length (m): "); 212 | double len = 0; 213 | scanf("%lf", &len); 214 | scene->cols = len*chirp->rows/variables->signal_distance; 215 | 216 | printf("Enter area range (m): "); 217 | scanf("%lf", &len); 218 | scene->rows = len*chirp->rows/variables->signal_distance; 219 | 220 | if(scene->cols < 2){ 221 | printf("Invalid azimuth length, exiting.\n"); 222 | return; 223 | } 224 | 225 | if(scene->rows < chirp->rows){ 226 | printf("Too small range, exiting.\n"); 227 | return; 228 | } 229 | 230 | unsigned int rows = scene->rows; 231 | unsigned int cols = scene->cols; 232 | 233 | scene->data = malloc(rows*cols*sizeof(complex double)); 234 | 235 | printf("Scene range: %fm\n", variables->signal_distance*(rows/chirp->rows)); 236 | printf("Scene azimuth length: %fm\n", variables->signal_distance*(cols/chirp->rows)); 237 | 238 | memcpy(&scene->data[(unsigned int)(cols/2)*rows + (unsigned int)(rows/2) - chirp->rows/2], 239 | chirp->data, 240 | chirp->rows*sizeof(complex double)); 241 | } 242 | 243 | /* Pulse compression of a single waveform (it's nice to look at the plots to see that pulse compression works). 244 | * This is just convolution computed in the frequency domain with the FFT. 245 | */ 246 | void pulse_compress_signal(matrix* chirp, matrix* match, matrix* pc_waveform, radar_variables* variables){ 247 | pc_waveform->rows = chirp->rows; 248 | pc_waveform->cols = chirp->cols; 249 | pc_waveform->data = malloc(chirp->rows*sizeof(complex double)); 250 | 251 | unsigned int kernel_length = variables->chirp_samples; 252 | unsigned int filter_length = 2*kernel_length; 253 | 254 | fftw_complex* padded_signal = fftw_malloc(filter_length*sizeof(fftw_complex)); 255 | fftw_complex* padded_kernel = fftw_malloc(filter_length*sizeof(fftw_complex)); 256 | 257 | fftw_complex* sigfft = fftw_malloc(filter_length*sizeof(fftw_complex)); 258 | fftw_complex* matchfft = fftw_malloc(filter_length*sizeof(fftw_complex)); 259 | fftw_complex* product = fftw_malloc(filter_length*sizeof(fftw_complex)); 260 | 261 | fftw_plan sigp = fftw_plan_dft_1d(filter_length, padded_signal, sigfft, FFTW_FORWARD, FFTW_MEASURE); 262 | fftw_plan matchp = fftw_plan_dft_1d(filter_length, padded_kernel, matchfft, FFTW_FORWARD, FFTW_MEASURE); 263 | fftw_plan iff = fftw_plan_dft_1d(kernel_length, product, pc_waveform->data, FFTW_FORWARD, FFTW_MEASURE); 264 | 265 | memset(padded_signal, 0, filter_length*sizeof(fftw_complex)); 266 | memset(padded_kernel, 0, filter_length*sizeof(fftw_complex)); 267 | memcpy(padded_signal, chirp->data, kernel_length*sizeof(fftw_complex)); 268 | memcpy(padded_kernel, match->data, kernel_length*sizeof(fftw_complex)); 269 | 270 | for(unsigned int i = 0; i < filter_length; i++){ 271 | padded_signal[i] /= filter_length; 272 | padded_kernel[i] /= filter_length; 273 | } 274 | 275 | fftw_execute(sigp); 276 | fftw_execute(matchp); 277 | 278 | for(unsigned int i = 0; i < filter_length; i++){ 279 | product[i] = sigfft[i]*pow(-1,i)*conj(sigfft[i]); 280 | } 281 | 282 | fftw_execute(iff); 283 | 284 | fftw_destroy_plan(sigp); 285 | fftw_destroy_plan(matchp); 286 | fftw_destroy_plan(iff); 287 | 288 | fftw_free(sigfft); 289 | fftw_free(matchfft); 290 | fftw_free(product); 291 | } 292 | 293 | /* Convolution of the entire radar image with the matched waveform, again implemented with the FFT. */ 294 | void pulse_compress_image(matrix* radar_image, matrix* pc_image, matrix* match, radar_variables* variables){ 295 | unsigned int rows = radar_image->rows; 296 | unsigned int cols = radar_image->cols; 297 | pc_image->rows = rows; 298 | pc_image->cols = cols; 299 | pc_image->data = malloc(rows*cols*sizeof(double complex)); 300 | 301 | // Make sure input has valid values. 302 | unsigned int kernel_length = match->rows; 303 | unsigned int z; 304 | 305 | normalize_image(radar_image->data, rows, cols); 306 | 307 | unsigned int filter_length = rows + kernel_length; 308 | 309 | fftw_complex* padded_kernel = fftw_malloc(filter_length*sizeof(fftw_complex)); 310 | fftw_complex* kernel_fft = fftw_malloc(filter_length*sizeof(fftw_complex)); 311 | fftw_complex* product = fftw_malloc(filter_length*sizeof(fftw_complex)); 312 | 313 | memset(padded_kernel, 0, filter_length*sizeof(fftw_complex)); 314 | memset(kernel_fft, 0, filter_length*sizeof(fftw_complex)); 315 | memcpy(padded_kernel, match->data, kernel_length*sizeof(fftw_complex)); 316 | 317 | // Normalize before FFT. 318 | for(z = 0; z < kernel_length; z++){ 319 | padded_kernel[z] /= filter_length; 320 | } 321 | 322 | // Compute fft of filter kernel. 323 | fftw_plan kernelfft = fftw_plan_dft_1d(filter_length, padded_kernel, kernel_fft, FFTW_FORWARD, FFTW_ESTIMATE); 324 | fftw_execute(kernelfft); 325 | fftw_complex* output_column; 326 | fftw_complex* padded_column = fftw_malloc(filter_length*sizeof(fftw_complex)); 327 | fftw_complex* padded_column_fft = fftw_malloc(filter_length*sizeof(fftw_complex)); 328 | unsigned int i,j; 329 | for(i = 0; i < cols; i++){ 330 | double complex* column = &radar_image->data[i*rows]; 331 | 332 | output_column = &pc_image->data[i*rows]; 333 | memset(padded_column, 0, filter_length*sizeof(fftw_complex)); 334 | memcpy(padded_column, column, rows*sizeof(fftw_complex)); 335 | 336 | // Normalize before FFT. 337 | for(z = 0; z < filter_length; z++){ 338 | padded_column[z] /= filter_length; 339 | } 340 | 341 | memset(padded_column_fft, 0, filter_length*sizeof(fftw_complex)); 342 | fftw_plan colfft = fftw_plan_dft_1d(filter_length, padded_column, padded_column_fft, FFTW_FORWARD, FFTW_ESTIMATE); 343 | fftw_execute(colfft); 344 | 345 | for(j = 0; j < filter_length; j++){ 346 | product[j] = padded_column_fft[j]*kernel_fft[j]; 347 | } 348 | 349 | fftw_plan colifft = fftw_plan_dft_1d(rows, product, output_column, FFTW_BACKWARD, FFTW_ESTIMATE); 350 | fftw_execute(colifft); 351 | 352 | fftw_destroy_plan(colfft); 353 | fftw_destroy_plan(colifft); 354 | } 355 | 356 | fftw_free(product); 357 | fftw_free(padded_column_fft); 358 | fftw_free(padded_column); 359 | fftw_free(padded_kernel); 360 | 361 | fftw_destroy_plan(kernelfft); 362 | 363 | } -------------------------------------------------------------------------------- /src/algorithms.h: -------------------------------------------------------------------------------- 1 | #ifndef _algorithms_h 2 | #define _algorithms_h 3 | 4 | #include "common_types.h" 5 | 6 | void gbp(matrix* pc_image, matrix* sar_image, radar_variables* variables); 7 | void gbp_fft(matrix* sar_image, matrix* sar_image_fft, radar_variables* variables); 8 | void radar_imager(matrix* scene, matrix* radar_image, matrix* chirp, radar_variables* variables); 9 | void insert_waveform_in_scene(matrix* chirp, matrix* scene, radar_variables* variables); 10 | void pulse_compress_signal(matrix* chirp, matrix* match, matrix* pc_waveform, radar_variables* variables); 11 | void pulse_compress_image(matrix* radar_image, matrix* pc_image, matrix* match, radar_variables* variables); 12 | 13 | 14 | #endif -------------------------------------------------------------------------------- /src/common_functions.c: -------------------------------------------------------------------------------- 1 | #include "common_functions.h" 2 | -------------------------------------------------------------------------------- /src/common_functions.h: -------------------------------------------------------------------------------- 1 | #ifndef _common_functions_h 2 | #define _common_functions_h 3 | 4 | #include "common_types.h" 5 | 6 | #endif -------------------------------------------------------------------------------- /src/common_types.h: -------------------------------------------------------------------------------- 1 | #ifndef _common_types_h 2 | #define _common_types_h 3 | 4 | #include 5 | #include 6 | 7 | #define PI 3.14159265 8 | #define C 300000000 9 | 10 | typedef struct{ 11 | bool is_complex; 12 | unsigned int rows; 13 | unsigned int cols; 14 | }radar_metadata; 15 | 16 | // Just as the data_arrays structure, this struct keeps variables during the simulation run which is passed to member functions. 17 | typedef struct{ 18 | long unsigned int start_frequency; 19 | long unsigned int bandwidth; 20 | unsigned long int chirp_samples; 21 | unsigned int btproduct; 22 | int altitude; 23 | float beamwidth; 24 | double signal_distance; 25 | char radar_data_filename[255]; 26 | }radar_variables; 27 | 28 | typedef struct matrix{ 29 | complex double* data; 30 | unsigned int rows; 31 | unsigned int cols; 32 | char name[255]; 33 | }matrix; 34 | 35 | #endif -------------------------------------------------------------------------------- /src/file_io.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Alexander Rajula 3 | * Contact: alexander@rajula.org 4 | * 5 | * This code is ugly and really ought to be re-written. 6 | */ 7 | 8 | #include "file_io.h" 9 | #include "common_functions.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | void write_data(matrix* data_matrix){ 16 | FILE* fp; 17 | FILE* dimensions; 18 | 19 | char filename[255]; 20 | dimensions = fopen("output/dimensions.dat", "a"); 21 | 22 | memset(filename, 0, 255); 23 | strcat(filename, data_matrix->name); 24 | strcat(filename, ".dat"); 25 | 26 | fp = fopen(filename, "w"); 27 | 28 | for(int i = 0; i < data_matrix->cols; i++){ 29 | for(int j = 0; j < data_matrix->rows; j++){ 30 | fprintf(fp, FILE_OUTPUT_PRECISION, creal(data_matrix->data[i*data_matrix->rows+j])); 31 | fprintf(fp, FILE_OUTPUT_PRECISION, cimag(data_matrix->data[i*data_matrix->rows+j])); 32 | } 33 | fprintf(fp, "\n"); 34 | } 35 | 36 | fclose(fp); 37 | 38 | fprintf(dimensions, "%s\n%i\n%i\n", data_matrix->name, data_matrix->rows, data_matrix->cols); 39 | 40 | fclose(dimensions); 41 | 42 | return; 43 | } 44 | -------------------------------------------------------------------------------- /src/file_io.h: -------------------------------------------------------------------------------- 1 | #ifndef _file_io_h 2 | #define _file_io_h 3 | 4 | #include "common_types.h" 5 | 6 | #define FILE_OUTPUT_PRECISION "%.10f\t" 7 | 8 | void write_data(matrix* data_matrix); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/filters.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Alexander Rajula 3 | * Contact: alexander@rajula.org 4 | */ 5 | 6 | #include "filters.h" 7 | #include 8 | 9 | /* In the processing stages in the simulator, amplitudes may rise dangerously high and cause overflow if not compensated for. 10 | * This code simply normalizes the input image by the mean. */ 11 | void normalize_image(complex double* image, unsigned int rows, unsigned int cols){ 12 | double* colmeans = malloc(cols*sizeof(complex double)); 13 | double rowmean = 0; 14 | double mean = 0; 15 | 16 | for(int i = 0; i < cols; i++){ 17 | rowmean = 0; 18 | 19 | for(int j = 0; j < rows; j++){ 20 | rowmean+= image[i*rows+j]; 21 | } 22 | 23 | rowmean /= rows; 24 | colmeans[i] = rowmean; 25 | } 26 | 27 | for(int i = 0; i < cols; i++){ 28 | mean += colmeans[i]; 29 | } 30 | 31 | mean /= cols; 32 | 33 | free(colmeans); 34 | 35 | for(int i = 0; i < cols; i++){ 36 | for(int j = 0; j < rows; j++){ 37 | image[i*rows+j] /= mean; 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/filters.h: -------------------------------------------------------------------------------- 1 | #ifndef _filters_h 2 | #define _filters_h 3 | 4 | #include 5 | 6 | void normalize_image(complex double* image, unsigned int rows, unsigned int cols); 7 | 8 | #endif -------------------------------------------------------------------------------- /src/sar_simulator.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Alexander Rajula 3 | * Contact: alexander@rajula.org 4 | * 5 | * This simulator can either simulate and process simulated data, 6 | * or read pre-existing radar data from file, and then process it. 7 | * 8 | * The simulator utilizes standard SAR imaging and filtering algorithms, and the 9 | * processing chain typically looks something like this: 10 | * create an empty scene -> generate chirp waveform -> generate matched chirp waveform -> 11 | * pulse compress single chirp waveform -> insert original (uncompressed) waveform in the middle of the scene -> 12 | * employ radar scanning algorithm -> employ RFI filtering (if needed) -> 13 | * employ pulse compression (if needed) -> employ GBP algorithm -> employ apodization (if needed) -> 14 | * calculate 2D FFT of GBP image -> write *all* data to file 15 | * 16 | * This file (sar_simulator.c) contains no algorithms, it just helps the user input data needed for simulation and 17 | * processing. 18 | */ 19 | 20 | #include "common_functions.h" 21 | #include "sar_simulator.h" 22 | #include "file_io.h" 23 | #include "waveforms.h" 24 | #include "algorithms.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | matrix time_vector; 31 | matrix chirp; 32 | matrix match; 33 | matrix pc_waveform; 34 | matrix scene; 35 | matrix radar_image; 36 | matrix pc_image; 37 | matrix sar_image; 38 | matrix sar_image_fft; 39 | matrix chirp_fft; 40 | matrix match_fft; 41 | radar_variables variables; 42 | 43 | int main(int argc, char** argv){ 44 | strcpy(time_vector.name, "time_vector"); 45 | strcpy(chirp.name, "chirp"); 46 | strcpy(chirp_fft.name, "chirp_fft"); 47 | strcpy(match.name, "match"); 48 | strcpy(match_fft.name, "match_fft"); 49 | strcpy(radar_image.name, "radar_image"); 50 | strcpy(pc_image.name, "pc_image"); 51 | strcpy(sar_image.name, "sar_image"); 52 | strcpy(sar_image_fft.name, "sar_fft"); 53 | strcpy(scene.name, "scene"); 54 | strcpy(pc_waveform.name, "pulse_compressed_waveform"); 55 | 56 | simulate(); 57 | 58 | process_data(); 59 | 60 | write_data(&time_vector); 61 | write_data(&chirp); 62 | write_data(&match); 63 | write_data(&pc_waveform); 64 | write_data(&scene); 65 | write_data(&radar_image); 66 | write_data(&pc_image); 67 | write_data(&sar_image); 68 | write_data(&sar_image_fft); 69 | write_data(&chirp_fft); 70 | write_data(&match_fft); 71 | 72 | return 0; 73 | } 74 | 75 | void simulate(void){ 76 | chirp_generator(&time_vector, &chirp, &variables); 77 | chirp_matched_generator(&chirp, &match); 78 | 79 | chirp_fft.data = malloc(chirp.rows*sizeof(complex double)); 80 | chirp_fft.rows = chirp.rows; 81 | chirp_fft.cols = chirp.cols; 82 | 83 | 84 | match_fft.data = malloc(match.rows*sizeof(complex double)); 85 | match_fft.rows = chirp.rows; 86 | match_fft.cols = chirp.cols; 87 | 88 | fft_waveform(chirp.rows, chirp.data, chirp_fft.data); 89 | fft_waveform(match.rows, match.data, chirp_fft.data); 90 | 91 | pulse_compress_signal(&chirp, &match, &pc_waveform, &variables); 92 | 93 | printf("Compressed pulse resolution: %lfm\n", calculate_compressed_pulse_resolution(&pc_waveform, &variables)); 94 | 95 | insert_waveform_in_scene(&chirp, &scene, &variables); 96 | 97 | radar_imager(&scene, &radar_image, &chirp, &variables); 98 | 99 | return; 100 | } 101 | 102 | void process_data(void){ 103 | char pc = 0; 104 | 105 | printf("Do you want to enable pulse compression (y/n)? "); 106 | do{ 107 | scanf("%c", &pc); 108 | if(pc == 'y') 109 | break; 110 | else if(pc == 'n') 111 | break; 112 | }while(1); 113 | 114 | if(pc == 'y'){ 115 | printf("Pulse-compressing image ... "); 116 | pulse_compress_image(&radar_image, &pc_image, &match, &variables); 117 | printf("done.\n"); 118 | } 119 | 120 | gbp(&pc_image, &sar_image, &variables); 121 | 122 | printf("Generating 2D FFT of GBP image ... "); 123 | gbp_fft(&sar_image, &sar_image_fft, &variables); 124 | printf("done.\n"); 125 | } 126 | -------------------------------------------------------------------------------- /src/sar_simulator.h: -------------------------------------------------------------------------------- 1 | /* Author: Alexander Rajula 2 | * Contact: alexander@rajula.org 3 | * 4 | * This header file contains basic structures for managing all data structures in the simulator. 5 | */ 6 | 7 | #ifndef sar_simulator_h 8 | #define sar_simulator_h 9 | 10 | #include "common_types.h" 11 | 12 | void simulate(void); 13 | void process_data(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/waveforms.c: -------------------------------------------------------------------------------- 1 | /* Note to the reader: 2 | * To use the standard "complex" data type in C, complex.h must be included 3 | * before fftw3.h - otherwise a bunch of compiler warnings will arise. 4 | */ 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "common_functions.h" 13 | #include "waveforms.h" 14 | 15 | void fft_waveform(unsigned int kernel_length, double complex* kernel, double complex* output){ 16 | for(int i = 0; i < kernel_length; i++){ 17 | kernel[i] /= kernel_length; 18 | } 19 | 20 | fftw_plan fft = fftw_plan_dft_1d(kernel_length, kernel, output, FFTW_FORWARD, FFTW_ESTIMATE); 21 | fftw_execute(fft); 22 | fftw_destroy_plan(fft); 23 | 24 | for(int i = 0; i < kernel_length; i++){ 25 | kernel[i] *= kernel_length; 26 | } 27 | 28 | } 29 | 30 | void chirp_generator(matrix* time_vector, matrix* chirp, radar_variables* variables){ 31 | printf("Chirp start frequency (Hz): "); 32 | int ret = scanf("%li", &variables->start_frequency); 33 | 34 | if(variables->start_frequency < 0){ 35 | printf("Negative start frequency entered, closing.\n"); 36 | return; 37 | } 38 | 39 | printf("Chirp bandwidth (Hz): "); 40 | ret = scanf("%li", &variables->bandwidth); 41 | 42 | if(variables->bandwidth < 0){ 43 | printf("Negative bandwidth entered, closing.\n"); 44 | return; 45 | } 46 | 47 | printf("Chirp bandwidth-time product: "); 48 | ret = scanf("%u", &variables->btproduct); 49 | 50 | if(variables->btproduct < 1){ 51 | printf("Too small BT-product entered, closing.\n"); 52 | return; 53 | } 54 | 55 | double chirp_duration_s = (double)variables->btproduct/variables->bandwidth; 56 | double chirp_rate_hz_per_s = variables->bandwidth/(chirp_duration_s); 57 | unsigned int sample_frequency_hz = 2*variables->bandwidth; 58 | double sample_period = 1/(double)sample_frequency_hz; 59 | variables->chirp_samples = chirp_duration_s*sample_frequency_hz; 60 | variables->signal_distance = chirp_duration_s*C; 61 | 62 | printf("Chirp duration: %f (s)\n", chirp_duration_s); 63 | printf("Chirp rate: %lf (Hz/s)\n", chirp_rate_hz_per_s); 64 | printf("Sample frequency: %u (Hz)\n", sample_frequency_hz); 65 | printf("Chirp samples: %lf\n", variables->chirp_samples); 66 | printf("Uncompressed pulse resolution: %fm\n", variables->signal_distance); 67 | 68 | /* Allocate memory for the time vector and generate it. */ 69 | time_vector->rows = variables->chirp_samples; 70 | time_vector->cols = 1; 71 | time_vector->data = malloc(variables->chirp_samples*sizeof(complex double)); 72 | 73 | double last_time = 0; 74 | for(int i = 0; i < chirp_duration_s*sample_frequency_hz; i++){ 75 | time_vector->data[i] = last_time; 76 | last_time += sample_period; 77 | } 78 | 79 | /* Allocate memory for the chirp waveform and generate it. */ 80 | chirp->rows = variables->chirp_samples; 81 | chirp->cols = 1; 82 | chirp->data = malloc(variables->chirp_samples*sizeof(complex double)); 83 | 84 | for(int i = 0; i < variables->chirp_samples; i++){ 85 | double time = time_vector->data[i]; 86 | chirp->data[i] = cexp(_Complex_I*2*PI*(variables->start_frequency*time+chirp_rate_hz_per_s*time*time)); 87 | } 88 | 89 | } 90 | 91 | void chirp_matched_generator(matrix* chirp, matrix* match){ 92 | /* Performs the following operations: 93 | * Chirp waveform -> FFT -> Complex conjucation -> Division by number of rows -> IFFT -> Matched chirp waveform 94 | */ 95 | 96 | /* Execute FFT on the chirp waveform. */ 97 | complex double* chirpfft = malloc(chirp->rows*sizeof(complex double)); 98 | fftw_plan chirpfftp = fftw_plan_dft_1d(chirp->rows, chirp->data, chirpfft, FFTW_FORWARD, FFTW_ESTIMATE); 99 | fftw_execute(chirpfftp); 100 | 101 | /* Complex-conjugate and divide by the number of rows. */ 102 | for(int z = 0; z < chirp->rows; z++){ 103 | chirpfft[z] = conj(chirpfft[z])/chirp->rows; 104 | } 105 | 106 | /* Allocate memory for the matched waveform */ 107 | match->rows = chirp->rows; 108 | match->cols = chirp->cols; 109 | match->data = malloc(match->rows*sizeof(complex double)); 110 | 111 | /* Execute IFFT to get the matched waveform. */ 112 | fftw_plan matchedp = fftw_plan_dft_1d(chirp->rows, chirpfft, match->data, FFTW_BACKWARD, FFTW_ESTIMATE); 113 | fftw_execute(matchedp); 114 | 115 | free(chirpfft); 116 | 117 | fftw_destroy_plan(chirpfftp); 118 | fftw_destroy_plan(matchedp); 119 | } 120 | 121 | float calculate_compressed_pulse_resolution(matrix* pc_waveform, radar_variables* variables){ 122 | double complex* waveform = pc_waveform->data; 123 | 124 | // Find maximum amplitude position 125 | int max_position = 0; 126 | double max_amplitude = 0; 127 | int low_index = 0; 128 | int high_index = 0; 129 | 130 | for(int i = 0; i < pc_waveform->rows; i++){ 131 | if( sqrt( pow(creal(waveform[i]), 2) + pow(cimag(waveform[i]),2) ) > max_amplitude){ 132 | max_amplitude = waveform[i]; 133 | max_position = i; 134 | } 135 | } 136 | 137 | double half_amplitude = max_amplitude / 2; 138 | double low_value; 139 | for(int i = max_position; i >= 0; i--){ 140 | low_value = sqrt( pow(creal(waveform[i]), 2) + pow(cimag(waveform[i]),2)); 141 | if( low_value <= half_amplitude){ 142 | low_index = i; 143 | break; 144 | } 145 | } 146 | 147 | for(int i = max_position; i < variables->chirp_samples; i++){ 148 | low_value = sqrt( pow(creal(waveform[i]), 2) + pow(cimag(waveform[i]),2)); 149 | if( low_value <= half_amplitude){ 150 | high_index = i; 151 | break; 152 | } 153 | } 154 | 155 | return variables->signal_distance*(high_index-low_index)/pc_waveform->rows; 156 | } 157 | -------------------------------------------------------------------------------- /src/waveforms.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Alexander Rajula 3 | * Contact: alexander@rajula.se 4 | */ 5 | 6 | #ifndef _waveforms_h 7 | #define _waveforms_h 8 | 9 | #include "common_types.h" 10 | 11 | void chirp_generator(matrix* time_vector, matrix* chirp, radar_variables* variables); 12 | void chirp_matched_generator(matrix* chirp, matrix* match); 13 | float calculate_compressed_pulse_resolution(matrix* pc_waveform, radar_variables* variables); 14 | void fft_waveform(unsigned int kernel_length, double complex* kernel, double complex* output); 15 | 16 | #endif --------------------------------------------------------------------------------