├── .gitignore ├── docs ├── README.md ├── _config.yml └── book_cover_small.png ├── matlab ├── Chapter1 │ └── interpolation_example.mlx ├── Chapter2 │ ├── spotlight_doppler_bandwidth_example.mlx │ ├── spotlight_prf_example.mlx │ ├── spotlight_psf_example.mlx │ ├── spotlight_resolution_example.mlx │ ├── spotlight_snr_example.mlx │ ├── stripmap_clutter_to_noise_example.mlx │ ├── stripmap_doppler_bandwidth_example.mlx │ ├── stripmap_nesz_example.mlx │ ├── stripmap_prf_example.mlx │ ├── stripmap_psf_example.mlx │ ├── stripmap_range_resolution_example.mlx │ ├── stripmap_signal_to_noise_example.mlx │ └── stripmap_support_band_example.mlx ├── Chapter3 │ ├── backprojection_example.mlx │ ├── backprojection_point_targets_example.mlx │ ├── polar_format_example.mlx │ ├── polar_format_point_targets_example.mlx │ ├── range_doppler_point_target_example.mlx │ ├── range_profile_backhoe_example.mlx │ └── range_profile_point_target_example.mlx ├── Chapter4 │ ├── backprojection_example.mlx │ └── polar_format_point_targets_example.mlx ├── Chapter5 │ ├── inverse_filtering_example.mlx │ ├── map_drift_example.mlx │ ├── minimum_entropy_example.mlx │ └── phase_gradient_example.mlx ├── Chapter7 │ ├── extended_range_example.mlx │ ├── nesz_example.mlx │ ├── rain_attenuation_example.mlx │ ├── rgiqe_example.mlx │ ├── spatial_resolution_example.mlx │ └── windowing_example.mlx └── Libs │ ├── Chapter 2 │ ├── range_resolution_ground_plane.m │ ├── range_resolution_slant_plane.m │ ├── spotlight_doppler_bandwidth.m │ ├── spotlight_snr.m │ ├── spotlight_snr_image.m │ ├── stripmap_cnr.m │ ├── stripmap_doppler_bandwidth.m │ ├── stripmap_nesz.m │ ├── stripmap_snr.m │ └── stripmap_support_band.m │ ├── Chapter 3 │ ├── backprojection.m │ └── backprojection_cv.m │ ├── Chapter 4 │ ├── backprojection_3d.m │ └── plot_iso.m │ ├── Chapter 5 │ └── pga.m │ ├── Chapter 7 │ └── rain_attenuation.m │ ├── db.m │ ├── lin.m │ └── sinc.m └── python ├── Chapter1 └── interpolation_example.ipynb ├── Chapter2 ├── spotlight_doppler_bandwidth.ipynb ├── spotlight_prf_example.ipynb ├── spotlight_psf_example.ipynb ├── spotlight_resolution_example.ipynb ├── spotlight_snr_example.ipynb ├── stripmap_clutter_to_noise_example.ipynb ├── stripmap_doppler_bandwidth_example.ipynb ├── stripmap_nesz_example.ipynb ├── stripmap_prf_example.ipynb ├── stripmap_psf_example.ipynb ├── stripmap_range_resolution_example.ipynb ├── stripmap_signal_to_noise_example.ipynb └── stripmap_support_band_example.ipynb ├── Chapter3 ├── backprojection.py ├── backprojection_example.ipynb ├── backprojection_point_targets_example.ipynb ├── polar_format_example.ipynb ├── polar_format_point_targets_example.ipynb ├── range_doppler_point_target_example.ipynb ├── range_profile_backhoe_example.ipynb ├── range_profile_point_target_example.ipynb └── signal.mat ├── Chapter4 ├── backprojection.py ├── backprojection_example.ipynb └── polar_format_point_targets_example.ipynb ├── Chapter5 ├── inverse_filtering_example.ipynb ├── map_drift_example.ipynb ├── minimum_entropy_example.ipynb ├── phase_gradient.py └── phase_gradient_example.ipynb ├── Chapter6 ├── ICEYE_Harris_1.jpg ├── ICEYE_Harris_2.jpg ├── ICEYE_ORB.jpg ├── ICEYE_Phase_Correlation.jpg ├── ICEYE_SIFT.jpg ├── harris_corner_example.ipynb ├── orb_example.ipynb ├── phase_correlation_example.ipynb └── sift_example.ipynb └── Chapter7 ├── extended_range_example.ipynb ├── nesz_example.ipynb ├── rain.py ├── rain_attenuation_example.ipynb ├── rgiqe_example.ipynb ├── spatial_resolution_example.ipynb └── windowing_example.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | *.mat 2 | *.sig 3 | *checkpoint* 4 | *_pycache_* 5 | LoadMonostaticSignatureFile.m 6 | *.h5 7 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Introduction to Synthetic Aperture Radar Using Python and MATLAB 2 | 3 | ### Artech House 4 | ISBN: 9781630818647 5 | 6 | Copyright: 2022 7 | 8 | [Order Now](https://us.artechhouse.com/Introduction-to-Synthetic-Aperture-Radar-Using-Python-and-MATLAB-P2293.aspx) 9 | 10 | ![Book_logo](book_cover_small.png) 11 | 12 | ## Python® 13 | 14 | The Python tools were developed with [Google Colab](https://colab.research.google.com/notebooks/intro.ipynb). The tool suite makes use of packages including SciPy, NumPy and Matplotlib. 15 | 16 | ## MATLAB® 17 | 18 | The MATLAB Live Editor scripts associated with this text were developed with R2020b and do not require any additional toolboxes. The folder structure for the MATLAB and Python scripts is identical. or more information on MATLAB visit [MathWorks®](https://www.mathworks.com/). 19 | 20 | ## DATA 21 | Data for examples are hosted by AFRL's [Sendor Data Management System](https://www.sdms.afrl.af.mil/index.php), provided by [ICEYE](https://www.iceye.com/), and [Tripoint Industries](http://tripointindustries.com/). 22 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | title: Introduction to Synthetic Aperture Radar Using Python and MATLAB 2 | description: by Andy Harrison 3 | theme: jekyll-theme-cayman 4 | -------------------------------------------------------------------------------- /docs/book_cover_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/docs/book_cover_small.png -------------------------------------------------------------------------------- /matlab/Chapter1/interpolation_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter1/interpolation_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/spotlight_doppler_bandwidth_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/spotlight_doppler_bandwidth_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/spotlight_prf_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/spotlight_prf_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/spotlight_psf_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/spotlight_psf_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/spotlight_resolution_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/spotlight_resolution_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/spotlight_snr_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/spotlight_snr_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/stripmap_clutter_to_noise_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/stripmap_clutter_to_noise_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/stripmap_doppler_bandwidth_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/stripmap_doppler_bandwidth_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/stripmap_nesz_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/stripmap_nesz_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/stripmap_prf_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/stripmap_prf_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/stripmap_psf_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/stripmap_psf_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/stripmap_range_resolution_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/stripmap_range_resolution_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/stripmap_signal_to_noise_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/stripmap_signal_to_noise_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter2/stripmap_support_band_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter2/stripmap_support_band_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter3/backprojection_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter3/backprojection_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter3/backprojection_point_targets_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter3/backprojection_point_targets_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter3/polar_format_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter3/polar_format_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter3/polar_format_point_targets_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter3/polar_format_point_targets_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter3/range_doppler_point_target_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter3/range_doppler_point_target_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter3/range_profile_backhoe_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter3/range_profile_backhoe_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter3/range_profile_point_target_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter3/range_profile_point_target_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter4/backprojection_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter4/backprojection_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter4/polar_format_point_targets_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter4/polar_format_point_targets_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter5/inverse_filtering_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter5/inverse_filtering_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter5/map_drift_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter5/map_drift_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter5/minimum_entropy_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter5/minimum_entropy_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter5/phase_gradient_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter5/phase_gradient_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter7/extended_range_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter7/extended_range_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter7/nesz_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter7/nesz_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter7/rain_attenuation_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter7/rain_attenuation_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter7/rgiqe_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter7/rgiqe_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter7/spatial_resolution_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter7/spatial_resolution_example.mlx -------------------------------------------------------------------------------- /matlab/Chapter7/windowing_example.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/matlab/Chapter7/windowing_example.mlx -------------------------------------------------------------------------------- /matlab/Libs/Chapter 2/range_resolution_ground_plane.m: -------------------------------------------------------------------------------- 1 | function [range_resolution] = range_resolution_ground_plane(waveform_bandwidth, look_angle) 2 | %RANGE_RESOLUTION_GROUND_PLANE: calculates the range resolution in the 3 | %ground plane for stripmap SAR imaging 4 | % Inputs: 5 | % waveform_bandwidth: bandwidth of the waveform (Hz) 6 | % look_angle: look angle measured from the platform, Figure 2.2 (deg) 7 | % 8 | % Outputs: 9 | % Range resolution in the ground plane (m) 10 | % 11 | % This function implements Equation (2.3) 12 | % 13 | % Created by: Andy Harrison (2/12/2021) 14 | % 15 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 16 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 17 | % and can not be copied and/or distributed without the express permission of Artech House. 18 | 19 | % Calculate the range resolution (m) 20 | range_resolution = 299792458 ./ (2 * waveform_bandwidth * cosd(90 - look_angle)); 21 | 22 | end 23 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 2/range_resolution_slant_plane.m: -------------------------------------------------------------------------------- 1 | function [range_resolution] = range_resolution_slant_plane(waveform_bandwidth) 2 | %RANGE_RESOLUTION_SLANT_PLANE: calculates the range resolution in the slant 3 | %plane for stripmap SAR imaging 4 | % 5 | % Inputs: 6 | % waveform_bandwidth: bandwidth of the waveform (Hz) 7 | % 8 | % Outputs: 9 | % Range resolution in the slant plane (m) 10 | % 11 | % This function implements Equation (2.2) 12 | % 13 | % Created by: Andy Harrison (2/12/2021) 14 | % 15 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 16 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 17 | % and can not be copied and/or distributed without the express permission of Artech House. 18 | 19 | % Calculate the range resolution (m) 20 | range_resolution = 299792458 ./ (2 * waveform_bandwidth); 21 | 22 | end 23 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 2/spotlight_doppler_bandwidth.m: -------------------------------------------------------------------------------- 1 | function [doppler_bandwidth] = spotlight_doppler_bandwidth(velocity, aperture_length, operating_frequency, slant_range) 2 | %SPOTLIGHT_DOPPLER_BANDWIDTH: calculates the Doppler bandwidth for spotlight SAR 3 | % Inputs: 4 | % velocity: target velocity (m/s) 5 | % aperture_length: synthetic aperture length (m) 6 | % operating_frequency: radar operating frequency (Hz) 7 | % slant_range: slant range to the imaging area (m) 8 | % 9 | % Outputs: 10 | % Doppler bandwidth (Hz) 11 | % 12 | % This function implements Equation (2.39) 13 | % 14 | % Created by: Andy Harrison (2/20/2021) 15 | % 16 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 17 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 18 | % and can not be copied and/or distributed without the express permission of Artech House. 19 | 20 | % Calculate the wavelength (m) 21 | wavelength = 299792458 / operating_frequency; 22 | 23 | % Calculate the Doppler bandwidth 24 | doppler_bandwidth = 2 * velocity * aperture_length / (wavelength * slant_range); 25 | end 26 | 27 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 2/spotlight_snr.m: -------------------------------------------------------------------------------- 1 | function [snr] = spotlight_snr(average_power, effective_aperture, rcs, operating_frequency, slant_range, ... 2 | system_temperature, receiver_bandwidth, noise_figure, losses) 3 | %SPOTLIGHT_SNR Calcultes the signal-to-noise ratio for spotlight SAR imaging 4 | % Inputs: 5 | % average_power: average power transmitted by the radar (W) 6 | % effective_aperture: the antenna effective aperture (m^2) 7 | % rcs: the radar cross section of the target (m^2) 8 | % cross_range_resolution: the cross-range resolution of the SAR system (m) 9 | % operating_frequency: the radar operating frequency (Hz) 10 | % slant_range: slant range to the imaged area (m) 11 | % system_temperature: the system or reference temperature (K) 12 | % receiver_bandwidth: the bandwidth of the receiver (Hz) 13 | % noise_figure: the receiver noise figure (dB) 14 | % losses: losses in the SAR system (dB) 15 | % 16 | % Outputs: 17 | % Signal-to-noise ratio 18 | % 19 | % This function implements Equation (2.42) 20 | % 21 | % Created by: Andy Harrison (2/20/2021) 22 | % 23 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 24 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 25 | % and can not be copied and/or distributed without the express permission of Artech House. 26 | 27 | % Calculate the wavelength (m) 28 | wavelength = 299792458 / operating_frequency; 29 | 30 | % Boltzmann's constant 31 | k = 1.380649e-23; 32 | 33 | % Calculate the clutter-to-noise ratio 34 | snr = (average_power * effective_aperture^2 * rcs) ./ ... 35 | (4 * pi * wavelength^2 * slant_range.^4 * k * system_temperature * receiver_bandwidth * noise_figure * losses); 36 | 37 | end 38 | 39 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 2/spotlight_snr_image.m: -------------------------------------------------------------------------------- 1 | function [snr_image] = spotlight_snr_image(average_power, effective_aperture, rcs, pulsewidth, synthetic_aperture_length, prf, ... 2 | operating_frequency, slant_range, system_temperature, noise_figure, loss_system, loss_processing, velocity) 3 | %SPOTLIGHT_SNR Calcultes the signal-to-noise ratio for spotlight SAR imaging 4 | % Inputs: 5 | % average_power: average power transmitted by the radar (W) 6 | % effective_aperture: the antenna effective aperture (m^2) 7 | % rcs: the radar cross section of the target (m^2) 8 | % pulsewidth: transmitted pulsewidth (s) 9 | % synthetic_aperture_length: length of the synthetic aperture (m) 10 | % prf: pulse repetition frequency (Hz) 11 | % operating_frequency: the radar operating frequency (Hz) 12 | % slant_range: slant range to the imaged area (m) 13 | % system_temperature: the system or reference temperature (K) 14 | % receiver_bandwidth: the bandwidth of the receiver (Hz) 15 | % noise_figure: the receiver noise figure 16 | % loss_system: losses in the SAR system 17 | % velocity: platform velocity (m/s) 18 | % 19 | % Outputs: 20 | % Signal-to-noise ratio 21 | % 22 | % This function implements Equation (2.47) 23 | % 24 | % Created by: Andy Harrison (2/20/2021) 25 | % 26 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 27 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 28 | % and can not be copied and/or distributed without the express permission of Artech House. 29 | 30 | % Calculate the wavelength (m) 31 | wavelength = 299792458 / operating_frequency; 32 | 33 | % Boltzmann's constant 34 | k = 1.380649e-23; 35 | 36 | % Calculate the clutter-to-noise ratio 37 | snr_image = (average_power * effective_aperture^2 * rcs * pulsewidth * synthetic_aperture_length * prf) ./ ... 38 | (4 * pi * wavelength^2 * slant_range.^4 * k * system_temperature * noise_figure * loss_system * loss_processing * velocity); 39 | 40 | end 41 | 42 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 2/stripmap_cnr.m: -------------------------------------------------------------------------------- 1 | function [cnr] = stripmap_cnr(average_power, effective_aperture, backscatter_coefficient, range_resolution, ... 2 | operating_frequency, slant_range, velocity, system_temperature, noise_figure, losses) 3 | %STRIPMAP_CNR: Calcultes the clutter-to-noise ratio for stripmap SAR imaging 4 | % Inputs: 5 | % average_power: average power transmitted by the radar (W) 6 | % effective_aperture: the antenna effective aperture (m^2) 7 | % backscatter_coefficient: the backscattering coefficient for distributed clutter 8 | % range_resolution: the range resolution of the SAR system (m) 9 | % operating_frequency: the radar operating frequency (Hz) 10 | % slant_range: slant range to the imaged area (m) 11 | % velocity: the velocity of the SAR platform (m/s) 12 | % system_temperature: the system or reference temperature (K) 13 | % noise_figure: the receiver noise figure (dB) 14 | % losses: losses in the SAR system (dB) 15 | % 16 | % Outputs: 17 | % Clutter-to-noise ratio 18 | % 19 | % This function implements Equation (2.26) 20 | % 21 | % Created by: Andy Harrison (2/20/2021) 22 | % 23 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 24 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 25 | % and can not be copied and/or distributed without the express permission of Artech House. 26 | 27 | % Calculate the wavelength (m) 28 | wavelength = 299792458 / operating_frequency; 29 | 30 | % Boltzmann's constant 31 | k = 1.380649e-23; 32 | 33 | % Calculate the clutter-to-noise ratio 34 | cnr = (average_power * effective_aperture^2 * backscatter_coefficient * range_resolution) ./ ... 35 | (8 * pi * wavelength * slant_range^3 * velocity * system_temperature * k * lin(noise_figure) * lin(losses)); 36 | 37 | end 38 | 39 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 2/stripmap_doppler_bandwidth.m: -------------------------------------------------------------------------------- 1 | function [doppler_bandwidth] = stripmap_doppler_bandwidth(velocity, frequency, beamwidth) 2 | %STRIPMAP_DOPPLER_BANDWIDTH: calculates the Doppler bandwidth for stripmap SAR 3 | % Inputs: 4 | % velocity: target velocity (m/s) 5 | % frequency: radar operating frequency (Hz) 6 | % beamwidth: radar antenna beamwidth (deg) 7 | % 8 | % Outputs: 9 | % Doppler bandwidth (Hz) 10 | % 11 | % This function implements Equation (2.17) 12 | % 13 | % Created by: Andy Harrison (2/12/2021) 14 | % 15 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 16 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 17 | % and can not be copied and/or distributed without the express permission of Artech House. 18 | 19 | % Calculate the wavelength (m) 20 | speed_of_light = 299792458; 21 | wavelength = speed_of_light / frequency; 22 | 23 | 24 | % Calculate the Doppler bandwidth 25 | doppler_bandwidth = 4 * velocity / wavelength * sind(0.5 * beamwidth); 26 | end 27 | 28 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 2/stripmap_nesz.m: -------------------------------------------------------------------------------- 1 | function [nesz] = stripmap_nesz(average_power, effective_aperture, range_resolution, ... 2 | operating_frequency, slant_range, velocity, system_temperature, noise_figure, losses) 3 | %STRIPMAP_NESZ: Calcultes the noise equivalent sigma_0 for stripmap SAR imaging 4 | % Inputs: 5 | % average_power: average power transmitted by the radar (W) 6 | % effective_aperture: the antenna effective aperture (m^2) 7 | % backscatter_coefficient: the backscattering coefficient for distributed clutter 8 | % range_resolution: the range resolution of the SAR system (m) 9 | % operating_frequency: the radar operating frequency (Hz) 10 | % slant_range: slant range to the imaged area (m) 11 | % velocity: the velocity of the SAR platform (m/s) 12 | % system_temperature: the system or reference temperature (K) 13 | % noise_figure: the receiver noise figure (dB) 14 | % losses: losses in the SAR system (dB) 15 | % 16 | % Outputs: 17 | % Noise equivalent sigma_0 18 | % 19 | % This function implements Equation (2.26) 20 | % 21 | % Created by: Andy Harrison (2/20/2021) 22 | % 23 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 24 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 25 | % and can not be copied and/or distributed without the express permission of Artech House. 26 | 27 | % Calculate the wavelength (m) 28 | wavelength = 299792458 / operating_frequency; 29 | 30 | % Boltzmann's constant 31 | k = 1.380649e-23; %physconst('Boltzmann'); 32 | 33 | % Calculate the noise equivalent sigma_0 34 | nesz = (8 * pi * wavelength * slant_range^3 * velocity * system_temperature * k * lin(noise_figure) * lin(losses)) / ... 35 | (average_power * effective_aperture^2 * range_resolution); 36 | end 37 | 38 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 2/stripmap_snr.m: -------------------------------------------------------------------------------- 1 | function [snr] = stripmap_snr(average_power, effective_aperture, rcs, cross_range_resolution, ... 2 | operating_frequency, slant_range, velocity, system_temperature, noise_figure, losses) 3 | %STRIPMAP_SNR: Calcultes the signal-to-noise ratio for stripmap SAR imaging 4 | % Inputs: 5 | % average_power: average power transmitted by the radar (W) 6 | % effective_aperture: the antenna effective aperture (m^2) 7 | % rcs: the radar cross section of the target (m^2) 8 | % cross_range_resolution: the cross-range resolution of the SAR system (m) 9 | % operating_frequency: the radar operating frequency (Hz) 10 | % slant_range: slant range to the imaged area (m) 11 | % velocity: the velocity of the SAR platform (m/s) 12 | % system_temperature: the system or reference temperature (K) 13 | % noise_figure: the receiver noise figure 14 | % losses: losses in the SAR system 15 | % 16 | % Outputs: 17 | % Signal-to-noise ratio 18 | % 19 | % This function implements Equation (2.24) 20 | % 21 | % Created by: Andy Harrison (2/20/2021) 22 | % 23 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 24 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 25 | % and can not be copied and/or distributed without the express permission of Artech House. 26 | 27 | % Calculate the wavelength (m) 28 | wavelength = 299792458 / operating_frequency; 29 | 30 | % Boltzmann's constant 31 | k = 1.380649e-23; 32 | 33 | % Calculate the clutter-to-noise ratio 34 | snr = (average_power * effective_aperture^2 * rcs) ./ ... 35 | (8 * pi * wavelength * slant_range^3 * cross_range_resolution * velocity * system_temperature * k * noise_figure * losses); 36 | 37 | end 38 | 39 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 2/stripmap_support_band.m: -------------------------------------------------------------------------------- 1 | function [support_band] = stripmap_support_band(image_span, slant_range, frequency, antenna_width) 2 | %STRIPMAP_SUPPORT_BAND: calculates the support band for stripmap SAR 3 | %imaging 4 | % Inputs: 5 | % image_span: along-track image span (m) 6 | % slant_range: nominal slant-range to the swath center (m) 7 | % frequency: radar operating frequency (Hz) 8 | % antenna_width: width of the antenna in the along-track direction (m) 9 | % 10 | % Outputs: 11 | % Stripmap SAR support band (m) 12 | % 13 | % This function implements Equation (2.5) 14 | % 15 | % Created by: Andy Harrison (2/12/2021) 16 | % 17 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 18 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 19 | % and can not be copied and/or distributed without the express permission of Artech House. 20 | 21 | % Calculate the wavelength (m) 22 | wavelength = 299792458 / frequency; 23 | 24 | % Calculate the support band (m) 25 | support_band = [-0.5 * image_span - slant_range * wavelength / (2 * antenna_width), ... 26 | 0.5 * image_span + slant_range * wavelength / (2 * antenna_width)]; 27 | end 28 | 29 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 3/backprojection.m: -------------------------------------------------------------------------------- 1 | function [ bp_image ] = backprojection( signal, sensor_x, sensor_y, sensor_z, range_center, x_image, y_image, z_image, frequency, fft_length ) 2 | %% Reconstruct the two-dimensional image using the filtered backprojection method. 3 | % :param signal: The signal in K-space. 4 | % :param sensor_x: The sensor x-coordinate (m). 5 | % :param sensor_y: The sensor y-coordinate (m). 6 | % :param sensor_z: The sensor z-coordinate (m). 7 | % :param range_center: The range to the center of the image (m). 8 | % :param x_image: The x-coordinates of the image (m). 9 | % :param y_image: The y-coordinates of the image (m). 10 | % :param z_image: The z-coordinates of the image (m). 11 | % :param frequency: The frequency array (Hz). 12 | % :param fft_length: The number of points in the FFT. 13 | % :return: The reconstructed image. 14 | % 15 | % Created by: Lee A. Harrison 16 | % On: 2/9/2019 17 | % 18 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 19 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 20 | % and can not be copied and/or distributed without the express permission of Artech House. 21 | 22 | % Speed of light 23 | c = 299792458; 24 | 25 | % Get the frequency step size 26 | frequency_step = frequency(2) - frequency(1); 27 | 28 | % Calculate the maximum scene size and resolution 29 | range_extent = c / (2.0 * frequency_step); 30 | 31 | % Calculate the range window for the pulses 32 | range_window = linspace(-0.5 * range_extent, 0.5 * range_extent, fft_length); 33 | 34 | % Initialize the image 35 | bp_image = zeros(size(x_image)); 36 | 37 | % Loop over all pulses in the data 38 | term = 1j * 4.0 * pi * frequency(1) / c; 39 | 40 | if numel(range_center) == 1 41 | range_center = range_center * ones(length(sensor_x)); 42 | end 43 | 44 | for i = 1:length(sensor_x) 45 | 46 | % Calculate the range profile 47 | range_profile = fftshift(ifft(signal(:, i), fft_length)); 48 | 49 | % Calculate the range to each pixel 50 | range_image = sqrt((sensor_x(i) - x_image) .^ 2 + (sensor_y(i) - y_image) .^ 2 ... 51 | + (sensor_z(i) - z_image) .^ 2) - range_center(i); 52 | 53 | % Interpolate the range profile onto the image grid and multiply by the range phase 54 | % For large scenes, should check the range window and index 55 | bp_image = bp_image + interp1(range_window, range_profile, range_image, 'linear', 0.0) .* exp(term * range_image); 56 | 57 | % if rem(i, 100) == 0 58 | % fprintf('%d of %d\n', i, length(sensor_x)); 59 | % end 60 | 61 | end 62 | 63 | -------------------------------------------------------------------------------- /matlab/Libs/Chapter 3/backprojection_cv.m: -------------------------------------------------------------------------------- 1 | function [ bp_image ] = backprojection_cv( signal, sensor_az, sensor_el, ... 2 | x_image, y_image, z_image, frequency, fft_length) 3 | %% Reconstruct the two-dimensional image using the filtered backprojection method. 4 | % :param signal: The signal in K-space. 5 | % :param sensor_az: The sensor azimuth positions (rad). 6 | % :param sensor_el: The sensor elevation positions (rad). 7 | % :param x_image: The image x-coordinates (m). 8 | % :param y_image: The image y-coordinates (m). 9 | % :param z_image: The image z-coordinates (m). 10 | % :param frequency: The frequency array (Hz). 11 | % :param fft_length: The number of points in the FFT. 12 | % :return: The reconstructed image. 13 | % 14 | % Created by: Andy Harrison 15 | % On: 2/9/2019 16 | % 17 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 18 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 19 | % and can not be copied and/or distributed without the express permission of Artech House. 20 | 21 | % Speed of light 22 | c = 299792458; 23 | 24 | % Get the frequency step size 25 | frequency_step = frequency(2) - frequency(1); 26 | 27 | % Calculate the maximum scene size and resolution 28 | range_extent = c / (2.0 * frequency_step); 29 | 30 | % Calculate the range window for the pulses 31 | range_window = linspace(-0.5 * range_extent, 0.5 * range_extent, fft_length); 32 | 33 | % Initialize the image 34 | bp_image = zeros(size(x_image)); 35 | 36 | % Loop over all pulses in the data 37 | term = 1j * 4.0 * pi * frequency(1) / c; 38 | 39 | for i = 1:length(sensor_az) 40 | 41 | % Calculate the range profile 42 | range_profile = fftshift(ifft(signal(:, i), fft_length)); 43 | 44 | % Calculate the range to each pixel 45 | range_image = x_image * cos(sensor_el(i)) * cos(sensor_az(i)) + ... 46 | y_image * cos(sensor_el(i)) * sin(sensor_az(i)) + z_image * sin(sensor_el(i)); 47 | 48 | % Interpolate the range profile onto the image grid and multiply by the range phase 49 | % For large scenes, should check the range window and index 50 | bp_image = bp_image + interp1(range_window, range_profile, range_image, 'linear', 0.0) .* exp(term * range_image); 51 | 52 | % if rem(i, 100) == 0 53 | % fprintf('%d of %d\n', i, length(sensor_az)); 54 | % end 55 | 56 | end -------------------------------------------------------------------------------- /matlab/Libs/Chapter 4/backprojection_3d.m: -------------------------------------------------------------------------------- 1 | function [ bp_image ] = backprojection_3d( signal, az, el, ... 2 | x_image, y_image, z_image, frequency, fft_length) 3 | %% Reconstruct the three-dimensional image using the filtered backprojection method. 4 | % :param signal: The signal in K-space. 5 | % :param az: The sensor azimuth positions (rad). 6 | % :param el: The sensor elevation positions (rad). 7 | % :param x_image: The image x-coordinates (m). 8 | % :param y_image: The image y-coordinates (m). 9 | % :param z_image: The image z-coordinates (m). 10 | % :param frequency: The frequency array (Hz). 11 | % :param fft_length: The number of points in the FFT. 12 | % :return: The reconstructed image. 13 | % 14 | % Created by: Andy Harrison 15 | % On: 2/9/2019 16 | % 17 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 18 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 19 | % and can not be copied and/or distributed without the express permission of Artech House. 20 | 21 | % Speed of light 22 | c = 299792458; 23 | 24 | % Get the frequency step size 25 | frequency_step = frequency(2) - frequency(1); 26 | 27 | % Calculate the maximum scene size and resolution 28 | range_extent = c / (2.0 * frequency_step); 29 | 30 | % Calculate the range window for the pulses 31 | range_window = linspace(-0.5 * range_extent, 0.5 * range_extent, fft_length); 32 | 33 | % Initialize the image 34 | bp_image = zeros(size(x_image)); 35 | 36 | [nr, nc] = size(az); 37 | 38 | % Loop over all pulses in the data 39 | term = 1j * 4.0 * pi * frequency(1) / c; 40 | 41 | for row = 1:nr 42 | 43 | if mod(row, 10) == 0 44 | fprintf('Az = %d of %d\n', row, nr); 45 | end 46 | 47 | for col = 1:nc 48 | 49 | % Calculate the range profile 50 | range_profile = fftshift(ifft(signal(:, row, col), fft_length)); 51 | 52 | % Calculate the range to each pixel 53 | range_image = x_image * cos(el(row, col)) * cos(az(row, col)) + ... 54 | y_image * cos(el(row, col)) * sin(az(row, col)) + ... 55 | z_image * sin(el(row, col)); 56 | 57 | % Interpolate the range profile onto the image grid and multiply by the range phase 58 | % For large scenes, should check the range window and index 59 | bp_image = bp_image + interp1(range_window, range_profile, range_image, 'linear', 0.0) .* exp(term * range_image); 60 | end 61 | end 62 | 63 | end -------------------------------------------------------------------------------- /matlab/Libs/Chapter 4/plot_iso.m: -------------------------------------------------------------------------------- 1 | function plot_iso(x, y, z, signal, n) 2 | 3 | n_levels = n; 4 | 5 | t = linspace(0.4, 1.2, n_levels); 6 | 7 | levels = exp(t) .^ 4; 8 | 9 | levels = levels./max(levels); 10 | 11 | % levels = linspace(0.01, 0.2, n_levels); 12 | 13 | fa = levels; 14 | 15 | figure; 16 | 17 | for i = 1:n_levels 18 | 19 | iLevel = levels(i); 20 | 21 | p = patch(isosurface(x, y, z, signal, iLevel)); 22 | 23 | ind = floor(i/n_levels * 256); 24 | 25 | colormap 'jet'; 26 | cmap = colormap; 27 | c = squeeze(ind2rgb(ind, cmap)); 28 | 29 | p.FaceColor = c; 30 | p.EdgeColor = 'none'; 31 | p.FaceAlpha = fa(i); 32 | 33 | hold on 34 | 35 | end 36 | 37 | daspect([1 1 1]) 38 | view(3); 39 | axis equal; 40 | grid on 41 | camlight 42 | lighting gouraud -------------------------------------------------------------------------------- /matlab/Libs/Chapter 5/pga.m: -------------------------------------------------------------------------------- 1 | function [corrected_image, phase_error_estimate] = pga(original_image, threshold) 2 | save_image = original_image; 3 | 4 | index_center = ceil(size(original_image, 2)/2); 5 | 6 | % Circular shift 7 | [~, maximum_along_az_idx] = max(abs(original_image), [], 2); 8 | for i = 1:size(original_image, 1) 9 | original_image(i,:) = circshift(transpose(original_image(i,:)), index_center - maximum_along_az_idx(i)); 10 | end 11 | 12 | % Calculate the window width 13 | noncoherent_window = sum(abs(original_image).^2, 1); 14 | window_cutoff = max(20 * log10(noncoherent_window)) - abs(threshold); 15 | 16 | % Find indices for the window 17 | last_index = find(20 * log10(noncoherent_window(1:index_center )) - window_cutoff<0, 1, 'last' ); 18 | first_index = find(20 * log10(noncoherent_window(index_center+1:end)) - window_cutoff<0, 1, 'first'); 19 | 20 | last_index = last_index+1; 21 | first_index = first_index + index_center - 1; 22 | 23 | noncoherent_window = zeros(size(noncoherent_window)); 24 | noncoherent_window(last_index:first_index) = 1; 25 | 26 | % Set the FFT size 27 | nfft = size(original_image, 2); 28 | 29 | % Calculate the image spectrum 30 | image_spectrum = fft(ifftshift( original_image .* repmat(noncoherent_window, size(original_image,1), 1), 2), nfft, 2); 31 | 32 | % Calculate the delta phase and the error estimate 33 | delta_phase = angle( sum( conj(image_spectrum(:, 1:end-1)) .* image_spectrum(:, 2:end), 1) ); 34 | phase_error_estimate = [0 cumsum(delta_phase)]; 35 | 36 | % Calculate the linear phase term 37 | linear_coefs = polyfit(1:length(phase_error_estimate), phase_error_estimate, 1); 38 | 39 | % Updated phase error estimate 40 | phase_error_estimate = unwrap(phase_error_estimate - polyval(linear_coefs, 1:length(phase_error_estimate))); 41 | 42 | % Calculate the image spectrum 43 | image_spectrum = fft(ifftshift( save_image, 2), nfft, 2); 44 | 45 | % Correct image spectrum with phase estimate 46 | image_spectrum = image_spectrum .* repmat(exp(-1j*phase_error_estimate), size(save_image, 1), 1); 47 | 48 | % Finally compute the focused image 49 | corrected_image = fftshift(ifft(image_spectrum, [], 2), 2); 50 | 51 | end -------------------------------------------------------------------------------- /matlab/Libs/Chapter 7/rain_attenuation.m: -------------------------------------------------------------------------------- 1 | function attenuation = rain_attenuation(frequency, rain_rate, elevation_angle, polarization_tilt_angle) 2 | %% Calculate the attenuation due to rain. 3 | % :param frequency: The operating frequency (GHz). 4 | % :param rain_rate: The rain rate (mm/hr). 5 | % :param elevation_angle: The elevation angle (radians). 6 | % :param polarization_tilt_angle: The polarization tilt angle (radians). 7 | % :return: The specific attenuation due to rain (dB/km). 8 | % 9 | % Created by: Lee A. Harrison 10 | % On: 6/18/2018 11 | % 12 | % Copyright (C) 2022 Artech House (artech@artechhouse.com) 13 | % This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 14 | % and can not be copied and/or distributed without the express permission of Artech House. 15 | 16 | % Table 2.3 Coefficients for calculating k_h 17 | a_kh = [-5.33980, -0.35351, -0.23789, -0.94158]; 18 | b_kh = [-0.1008, 1.26970, 0.86036, 0.64552]; 19 | c_kh = [1.13098, 0.45400, 0.15354, 0.16817]; 20 | d_kh = -0.18961; 21 | e_kh = 0.71147; 22 | 23 | % Table 2.4 Coefficients for calculating k_v 24 | a_kv = [-3.80595, -3.44965, -0.39902, 0.50167]; 25 | b_kv = [0.56934, -0.22911, 0.73042, 1.07319]; 26 | c_kv = [0.81061, 0.51059, 0.11899, 0.27195]; 27 | d_kv = -0.16398; 28 | e_kv = 0.63297; 29 | 30 | % Table 2.5 Coefficients for calculating alpha_h 31 | a_ah = [-0.14318, 0.29591, 0.32177, -5.37610, 16.1721]; 32 | b_ah = [1.82442, 0.77564, 0.63773, -0.96230, -3.29980]; 33 | c_ah = [-0.55187, 0.19822, 0.13164, 1.47828, 3.43990]; 34 | d_ah = 0.67849; 35 | e_ah = -1.95537; 36 | 37 | % Table 2.6 Coefficients for calculating alpha_v 38 | a_av = [-0.07771, 0.56727, -0.20238, -48.2991, 48.5833]; 39 | b_av = [2.33840, 0.95545, 1.14520, 0.791669, 0.791459]; 40 | c_av = [-0.76284, 0.54039, 0.26809, 0.116226, 0.116479]; 41 | d_av = -0.053739; 42 | e_av = 0.83433; 43 | 44 | % Calculate k_h 45 | k_h = d_kh .* log10(frequency) + e_kh; 46 | for i = 1:numel(a_kh) 47 | k_h = k_h + a_kh(i) .* exp(-((log10(frequency) - b_kh(i)) ./ c_kh(i)) .^ 2); 48 | end 49 | k_h = 10.^k_h; 50 | 51 | % Calculate k_v 52 | k_v = d_kv .* log10(frequency) + e_kv; 53 | for i = 1:numel(a_kv) 54 | k_v = k_v + a_kv(i) .* exp(-((log10(frequency) - b_kv(i)) ./ c_kv(i)) .^ 2); 55 | end 56 | k_v = 10.^k_v; 57 | 58 | % Calculate alpha_h 59 | alpha_h = d_ah .* log10(frequency) + e_ah; 60 | for i = 1:numel(a_ah) 61 | alpha_h = alpha_h + a_ah(i) .* exp(-((log10(frequency) - b_ah(i)) / c_ah(i)) .^ 2); 62 | end 63 | 64 | % Calculate alpha_v 65 | alpha_v = d_av .* log10(frequency) + e_av; 66 | for i = 1:numel(a_av) 67 | alpha_v = alpha_v + a_av(i) .* exp(-((log10(frequency) - b_av(i)) / c_av(i)) .^ 2); 68 | end 69 | 70 | % Calculate k and alpha based on elevation angle and polarization 71 | k = 0.5 * (k_h + k_v + (k_h - k_v) * cos(elevation_angle)^2 * cos(2. * polarization_tilt_angle)); 72 | alpha = 0.5 * (k_h .* alpha_h + k_v .* alpha_v + (k_h .* alpha_h - k_v .* alpha_v) .* cos(elevation_angle)^2 .* cos(2. * polarization_tilt_angle)) ./ k; 73 | 74 | attenuation = k .* rain_rate.^alpha; -------------------------------------------------------------------------------- /matlab/Libs/db.m: -------------------------------------------------------------------------------- 1 | function [ x_db ] = db( x_lin ) 2 | %% Convert from linear units to decibels 3 | % :param x_lin: The value in linear units. 4 | % :return: The value in decibels. 5 | % 6 | % Created by: Lee A. Harrison 7 | % On: 6/18/2018 8 | % 9 | % Copyright (C) 2019 Artech House (artech@artechhouse.com) 10 | % This file is part of Introduction to Radar Using Python and MATLAB 11 | % and can not be copied and/or distributed without the express permission of Artech House. 12 | 13 | x_db = 10 * log10( x_lin ); 14 | 15 | end 16 | 17 | -------------------------------------------------------------------------------- /matlab/Libs/lin.m: -------------------------------------------------------------------------------- 1 | function [ x_lin ] = lin( x_db ) 2 | %% Convert from decibels to linear units 3 | % :param x_db: The value in decibels. 4 | % :return: The value in linear units. 5 | % 6 | % Created by: Lee A. Harrison 7 | % On: 6/18/2018 8 | % 9 | % Copyright (C) 2019 Artech House (artech@artechhouse.com) 10 | % This file is part of Introduction to Radar Using Python and MATLAB 11 | % and can not be copied and/or distributed without the express permission of Artech House. 12 | 13 | x_lin = 10 .^ (x_db ./ 10); 14 | 15 | end 16 | 17 | -------------------------------------------------------------------------------- /matlab/Libs/sinc.m: -------------------------------------------------------------------------------- 1 | function [ s ] = sinc( x ) 2 | %% Calculate the sinc function 3 | % :param x: Argument of the sinc function. 4 | % :return: The value of sinc(x). 5 | % 6 | % Created by: Andy Harrison 7 | % On: 6/18/2018 8 | % 9 | % Copyright (C) 2019 Artech House (artech@artechhouse.com) 10 | % This file is part of Introduction to Radar Using Python and MATLAB and 11 | % can not be copied and/or distributed without the express permission of Artech House. 12 | 13 | s = ones(size(x)); 14 | index = x ~= 0.0; 15 | s(index) = sin(pi * x(index)) ./ (pi * x(index)); 16 | 17 | end 18 | -------------------------------------------------------------------------------- /python/Chapter1/interpolation_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction to Synthetic Aperture Radar Using Python and MATLAB\n", 8 | "\n", 9 | "## by Andy Harrison - © Artech House 2022\n", 10 | "\n", 11 | "---\n", 12 | "\n", 13 | "## Example 1 Interpolation\n", 14 | "---" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "data": { 24 | "image/png": "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\n", 25 | "text/plain": [ 26 | "
" 27 | ] 28 | }, 29 | "metadata": { 30 | "needs_background": "light" 31 | }, 32 | "output_type": "display_data" 33 | } 34 | ], 35 | "source": [ 36 | "# Import each package\n", 37 | "import numpy as np\n", 38 | "import scipy.interpolate as interpolate\n", 39 | "from matplotlib import pyplot as plt\n", 40 | "\n", 41 | "# Set the figure size\n", 42 | "plt.rcParams[\"figure.figsize\"] = (12, 9)\n", 43 | "\n", 44 | "# Set the original data\n", 45 | "x = np.linspace(0, 10, 11)\n", 46 | "y = np.cos(x ** 2 / 9)\n", 47 | "\n", 48 | "# Define the new x values\n", 49 | "x_new = np.linspace(0, 10, 41)\n", 50 | "\n", 51 | "# Perform linear interpolation\n", 52 | "linear = interpolate.interp1d(x, y)\n", 53 | "\n", 54 | "# Perform cubic interpolation\n", 55 | "cubic = interpolate.interp1d(x, y, 'cubic')\n", 56 | "\n", 57 | "# Plot the results of the interpolation\n", 58 | "plt.plot(x, y, 'o', x_new, linear(x_new), '-', x_new, cubic(x_new), '--')\n", 59 | "plt.xlabel('X', size=16)\n", 60 | "plt.ylabel('Y', size=16)\n", 61 | "plt.title('Interpolation Example', size=18)\n", 62 | "\n", 63 | "# Display the legend\n", 64 | "plt.legend(['data', 'linear', 'cubic'], loc='best')\n", 65 | "\n", 66 | "# Set the tick label size\n", 67 | "plt.tick_params(labelsize=16)\n", 68 | "\n", 69 | "# Show the figure\n", 70 | "plt.show()" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [] 79 | } 80 | ], 81 | "metadata": { 82 | "kernelspec": { 83 | "display_name": "Python 3", 84 | "language": "python", 85 | "name": "python3" 86 | }, 87 | "language_info": { 88 | "codemirror_mode": { 89 | "name": "ipython", 90 | "version": 3 91 | }, 92 | "file_extension": ".py", 93 | "mimetype": "text/x-python", 94 | "name": "python", 95 | "nbconvert_exporter": "python", 96 | "pygments_lexer": "ipython3", 97 | "version": "3.8.5" 98 | } 99 | }, 100 | "nbformat": 4, 101 | "nbformat_minor": 4 102 | } 103 | -------------------------------------------------------------------------------- /python/Chapter2/spotlight_prf_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "id": "8rmpCcDss8IL" 7 | }, 8 | "source": [ 9 | "# Introduction to Synthetic Aperture Radar Using Python and MATLAB\n", 10 | "\n", 11 | "## by Andy Harrison - © Artech House 2022\n", 12 | "\n", 13 | "---\n", 14 | "\n", 15 | "## Example 2.12.12 Spotlight SAR Pulse Repetition Frequency\n", 16 | "---" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "id": "JFPTd4_P5WZm" 23 | }, 24 | "source": [ 25 | "**Import modules**" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 1, 31 | "metadata": { 32 | "id": "gZ11fHXD7vlr" 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "%matplotlib inline\n", 37 | "\n", 38 | "import numpy as np\n", 39 | "\n", 40 | "from scipy.constants import speed_of_light\n", 41 | "\n", 42 | "from matplotlib import pyplot as plt" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "**Set the operating frequency (Hz)**" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 2, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "operating_frequency = 12e9" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "**Calculate the wavelength (m)**" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": 3, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "wavelength = speed_of_light / operating_frequency" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "**Set the aperture length (m)**" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 4, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "aperture_length = 350" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "**Set the unambiguous range (m)**" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 5, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "unambiguous_range = 250e3;" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "**Set the velocity (m/s)**" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 6, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "velocity = np.linspace(50, 300)" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "**Set the slant range (m)**" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 7, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "slant_range = 20e3" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "**Calculate the upper limit (Hz)**" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": 8, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "prf_upper_limit = speed_of_light / (2 * unambiguous_range) * np.ones_like(velocity)" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "**Calculate the lower limit (Hz)**" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 9, 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "prf_lower_limit = 2 * velocity * aperture_length / (wavelength * slant_range)" 171 | ] 172 | }, 173 | { 174 | "cell_type": "markdown", 175 | "metadata": { 176 | "id": "z7mPvla36ilV" 177 | }, 178 | "source": [ 179 | "**Plot the upper and lower limtis**" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 10, 185 | "metadata": { 186 | "scrolled": false 187 | }, 188 | "outputs": [ 189 | { 190 | "data": { 191 | "image/png": "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h7H11lvz05/+FHj/SHtbWxtXXHEFd999N3vssQd33313n38fOkIek5aWFr0TD4ByDoNyTj9lHIbU53zlkBI97spe3+Wkk07ixz/+MQceeCBXXHEF3/zmN7nuuutobW1l1apVTJ06lbFjxzJ16lQmTJjARhttRH19PaeffjoXXXQREyZM4PXXX+ewww7jueeeA+DZZ59l2rRp1NXV9fjc3a1DPmrUKObNm8dFF13EKaecwvTp02ltbWWXXXbhS1/60trtqqurueqqq5gzZw433nhjr197V9SQi4iIiEjZrFy5khUrVnDggQcCcPLJJ3PccccBcMABBzB9+nSmTJnC1772Ne677z6cc0ycOBGABx54gGeffXbtY61atYrGxkYAjjrqqHU24z056qijANh1111pbGxk0KBBDBo0iJqaGlasWLHej1sINeQxqa+vj7sEKQPlHAblnH7KOAypz3k9jmSX26RJk5g6dSqvvfYaRx99ND/4wQ8wM4444gjAH92eOXMmtbW1H7pvoZ9uVFR0PWO7pqZm7e2d/915vdRLYmoOuYiIiIiUzZAhQxg2bBhTp04F4I477lh7tHzixInceeedbLvttlRUVDB8+HD++c9/MmHCBAA+9rGP8eMf/3jtY82bN6/s9QMMGjSI1atXF+3x1JDHpLm5Oe4SpAyUcxiUc/op4zAo59Jobm5m5MiRay/XXnstt99+O5dddhm77bYb8+bN44orrgD8lyudc0yaNAmACRMmMHToUIYNGwbADTfcwJw5c9htt93Yaaed1n7hcl1eeOGFtc+/5ZZb8oc//KFPr+nggw/m2WefLdqXOi2JZysqp7Fjx7o5c+aU/XmXLFnCiBEjyv68Ul7KOQzKOf2UcRjSmPNzzz3HjjvuGHcZidLe3k5VVVVJn6Or37uZPe6cG9vV9jpCHpO+fOlA+g/lHAblnH7KOAzKOQzdzSGPU/IqCkQS/xik+JRzGJRz+injMCjnMJhZ3CV8iP7yYtLU1BR3CVIGyjkMyjn9lHEYlHMY1qxZE3cJH6KGXERERCTFQv++YLmtz+9bDXlMcte3lPRSzmFQzumnjMOQxpxra2tZunSpmvIcpZya5Jxj6dKlXa6T3hOdGCgmlZX61YdAOYdBOaefMg5DGnMeOXIkixYtYvHixXGXkhjOuZLOI6+trWXkyJG9uk/6/vL6iaam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\n", 192 | "text/plain": [ 193 | "
" 194 | ] 195 | }, 196 | "metadata": { 197 | "needs_background": "light" 198 | }, 199 | "output_type": "display_data" 200 | } 201 | ], 202 | "source": [ 203 | "# Set the figure size\n", 204 | "\n", 205 | "plt.rcParams[\"figure.figsize\"] = (12, 9)\n", 206 | "\n", 207 | "\n", 208 | "\n", 209 | "# Upper and lower limits\n", 210 | "\n", 211 | "plt.plot(velocity, prf_upper_limit, linewidth=2, label='Upper Limit')\n", 212 | "\n", 213 | "plt.plot(velocity, prf_lower_limit, linewidth=2, label='Lower Limit')\n", 214 | "\n", 215 | "\n", 216 | "\n", 217 | "# Set the plot title and labels\n", 218 | "\n", 219 | "plt.title('Spotlight PRF Upper and Lower Limits', size=18)\n", 220 | "\n", 221 | "plt.xlabel('Platform Velocity (m/s)', size=16)\n", 222 | "\n", 223 | "plt.ylabel('Pulse Repetition Frequency (Hz)', size=16)\n", 224 | "\n", 225 | "\n", 226 | "\n", 227 | "# Set the tick label size\n", 228 | "\n", 229 | "plt.tick_params(labelsize=16)\n", 230 | "\n", 231 | "\n", 232 | "\n", 233 | "# Turn on the grid\n", 234 | "\n", 235 | "plt.grid(linestyle=':', linewidth=0.5)\n", 236 | "\n", 237 | "\n", 238 | "\n", 239 | "# Display the legend\n", 240 | "\n", 241 | "plt.legend(loc='best')\n", 242 | "\n", 243 | "\n", 244 | "\n", 245 | "# Display the figure\n", 246 | "\n", 247 | "plt.show()" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "metadata": {}, 254 | "outputs": [], 255 | "source": [] 256 | } 257 | ], 258 | "metadata": { 259 | "colab": { 260 | "name": "stripmap_range_resolution.ipynb", 261 | "provenance": [] 262 | }, 263 | "kernelspec": { 264 | "display_name": "Python 3", 265 | "language": "python", 266 | "name": "python3" 267 | }, 268 | "language_info": { 269 | "codemirror_mode": { 270 | "name": "ipython", 271 | "version": 3 272 | }, 273 | "file_extension": ".py", 274 | "mimetype": "text/x-python", 275 | "name": "python", 276 | "nbconvert_exporter": "python", 277 | "pygments_lexer": "ipython3", 278 | "version": "3.8.5" 279 | }, 280 | "vscode": { 281 | "interpreter": { 282 | "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" 283 | } 284 | } 285 | }, 286 | "nbformat": 4, 287 | "nbformat_minor": 1 288 | } 289 | -------------------------------------------------------------------------------- /python/Chapter2/stripmap_support_band_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "id": "8rmpCcDss8IL" 7 | }, 8 | "source": [ 9 | "# Introduction to Synthetic Aperture Radar Using Python and MATLAB\n", 10 | "\n", 11 | "## by Andy Harrison - © Artech House 2022\n", 12 | "\n", 13 | "---\n", 14 | "\n", 15 | "## Example 2.12.2 Stripmap SAR Support Band\n", 16 | "---" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "id": "JFPTd4_P5WZm" 23 | }, 24 | "source": [ 25 | "**Import modules**" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 1, 31 | "metadata": { 32 | "id": "gZ11fHXD7vlr" 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "from scipy.constants import speed_of_light\n", 37 | "\n", 38 | "from matplotlib import pyplot as plt" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": { 44 | "id": "z7mPvla36ilV" 45 | }, 46 | "source": [ 47 | "**Set the along-track distance (m)**" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": 2, 53 | "metadata": { 54 | "id": "oYudWiGX8Djw" 55 | }, 56 | "outputs": [], 57 | "source": [ 58 | "along_track = 100" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "**Set the nominal slant range (m)**" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": 3, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "slant_range = 10e3" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "**Set the cross-range resolution (m)**" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 4, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "cross_range_resolution = 1" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "**Set the operating frequency (Hz)**" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 5, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "frequency = 10e9" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": { 112 | "id": "zd1Qx7_16kcu" 113 | }, 114 | "source": [ 115 | "**Calculate the wavelength using**

\n", 116 | "\\begin{equation}\n", 117 | "\\lambda = \\frac{c}{f}\\hspace{0.1in}\\text{(m)}\n", 118 | "\\end{equation}" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 6, 124 | "metadata": { 125 | "id": "pmlh1gyJ8Ls9" 126 | }, 127 | "outputs": [], 128 | "source": [ 129 | "wavelength = speed_of_light / frequency" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "**Calculate the antenna dimension using**\n", 137 | "\n", 138 | "\\begin{equation}\n", 139 | "\\delta_a = \\frac{D_a}{2}\\hspace{0.1in}\\text{(m)}\\hspace{0.2in}(2.4)\n", 140 | "\\end{equation}" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 7, 146 | "metadata": {}, 147 | "outputs": [], 148 | "source": [ 149 | "antenna_width = 2 * cross_range_resolution" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "**Calculate the support band using**\n", 157 | "\n", 158 | "\\begin{equation}\n", 159 | "b\\in\\Big[ -\\frac{L_{span}}{2} -\\frac{R_0 \\lambda}{2D_a}, \\frac{L_{span}}{2} +\\frac{R_0 \\lambda}{2D_a}\\Big]\\hspace{0.1in}\\text{(m)}\\hspace{0.2in}(2.5)\n", 160 | "\\end{equation}" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": 8, 166 | "metadata": { 167 | "scrolled": true 168 | }, 169 | "outputs": [], 170 | "source": [ 171 | "b = [-0.5 * along_track - 0.5 * slant_range * wavelength / antenna_width, 0.5 * along_track + 0.5 * slant_range * wavelength / antenna_width]" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": {}, 177 | "source": [ 178 | "**Display the resutls**" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": 9, 184 | "metadata": {}, 185 | "outputs": [ 186 | { 187 | "name": "stdout", 188 | "output_type": "stream", 189 | "text": [ 190 | "Support Band = [-124.95, 124.95] (m)\n" 191 | ] 192 | } 193 | ], 194 | "source": [ 195 | "print('Support Band = [{:.2f}, {:.2f}] (m)'.format(b[0], b[1]))" 196 | ] 197 | } 198 | ], 199 | "metadata": { 200 | "colab": { 201 | "name": "stripmap_range_resolution.ipynb", 202 | "provenance": [] 203 | }, 204 | "kernelspec": { 205 | "display_name": "Python 3", 206 | "language": "python", 207 | "name": "python3" 208 | }, 209 | "language_info": { 210 | "codemirror_mode": { 211 | "name": "ipython", 212 | "version": 3 213 | }, 214 | "file_extension": ".py", 215 | "mimetype": "text/x-python", 216 | "name": "python", 217 | "nbconvert_exporter": "python", 218 | "pygments_lexer": "ipython3", 219 | "version": "3.8.5" 220 | } 221 | }, 222 | "nbformat": 4, 223 | "nbformat_minor": 1 224 | } 225 | -------------------------------------------------------------------------------- /python/Chapter3/backprojection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Project: SAR Book 3 | File: backprojection.py 4 | Created by: Lee A. Harrison 5 | On: 2/292019 6 | Created with: PyCharm 7 | 8 | Copyright (C) 2022 Artech House (artech@artechhouse.com) 9 | This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 10 | and can not be copied and/or distributed without the express permission of Artech House. 11 | """ 12 | from scipy.constants import c, pi 13 | from numpy import sqrt, linspace, zeros_like, exp, sin, cos, ones 14 | from scipy.interpolate import interp1d 15 | from scipy.fftpack import ifft, fftshift 16 | 17 | 18 | def reconstruct(signal, sensor_x, sensor_y, sensor_z, range_center, x_image, y_image, z_image, frequency, fft_length): 19 | """ 20 | Reconstruct the two-dimensional image using the filtered backprojection method. 21 | :param signal: The signal in K-space. 22 | :param sensor_x: The sensor x-coordinate (m). 23 | :param sensor_y: The sensor y-coordinate (m). 24 | :param sensor_z: The sensor z-coordinate (m). 25 | :param range_center: The range to the center of the image (m). 26 | :param x_image: The x-coordinates of the image (m). 27 | :param y_image: The y-coordinates of the image (m). 28 | :param z_image: The z-coordinates of the image (m). 29 | :param frequency: The frequency array (Hz). 30 | :param fft_length: The number of points in the FFT. 31 | :return: The reconstructed image. 32 | """ 33 | # Get the frequency step size 34 | frequency_step = frequency[1] - frequency[0] 35 | 36 | # Calculate the maximum scene size and resolution 37 | range_extent = c / (2.0 * frequency_step) 38 | 39 | # Calculate the range window for the pulses 40 | range_window = linspace(-0.5 * range_extent, 0.5 * range_extent, fft_length) 41 | 42 | # Initialize the image 43 | bp_image = zeros_like(x_image, dtype=complex) 44 | 45 | # Loop over all pulses in the data 46 | term = 1j * 4.0 * pi * frequency[0] / c 47 | 48 | # To work with stripmap 49 | if not isinstance(range_center, list): 50 | range_center *= ones(len(sensor_x)) 51 | 52 | index = 0 53 | for xs, ys, zs in zip(sensor_x, sensor_y, sensor_z): 54 | 55 | # Calculate the range profile 56 | range_profile = fftshift(ifft(signal[:, index], fft_length)) 57 | 58 | # Create the interpolation for this pulse 59 | f = interp1d(range_window, range_profile, kind='linear', bounds_error=False, fill_value=0.0) 60 | 61 | # Calculate the range to each pixel 62 | range_image = sqrt((xs - x_image) ** 2 + (ys - y_image) ** 2 + (zs - z_image) ** 2) - range_center[index] 63 | 64 | # Interpolate the range profile onto the image grid and multiply by the range phase 65 | # For large scenes, should check the range window and index 66 | bp_image += f(range_image) * exp(term * range_image) 67 | 68 | index += 1 69 | 70 | return bp_image 71 | 72 | 73 | def reconstruct2(signal, sensor_az, sensor_el, x_image, y_image, z_image, frequency, fft_length): 74 | """ 75 | Reconstruct the two-dimensional image using the filtered backprojection method. 76 | :param signal: The signal in K-space. 77 | :param sensor_az: The sensor azimuth positions (rad). 78 | :param sensor_el: The sensor elevation positions (rad). 79 | :param x_image: The image x-coordinates (m). 80 | :param y_image: The image y-coordinates (m). 81 | :param z_image: The image z-coordinates (m). 82 | :param frequency: The frequency array (Hz). 83 | :param fft_length: The number of points in the FFT. 84 | :return: The reconstructed image. 85 | """ 86 | # Get the frequency step size 87 | frequency_step = frequency[1] - frequency[0] 88 | 89 | # Calculate the maximum scene size and resolution 90 | range_extent = c / (2.0 * frequency_step) 91 | 92 | # Calculate the range window for the pulses 93 | range_window = linspace(-0.5 * range_extent, 0.5 * range_extent, fft_length) 94 | 95 | # Initialize the image 96 | bp_image = zeros_like(x_image, dtype=complex) 97 | 98 | # Loop over all pulses in the data 99 | term = 1j * 4.0 * pi * frequency[0] / c 100 | 101 | index = 0 102 | for az, el in zip(sensor_az, sensor_el): 103 | 104 | # Calculate the range profile 105 | range_profile = fftshift(ifft(signal[:, index], fft_length)) 106 | 107 | # Create the interpolation for this pulse 108 | f = interp1d(range_window, range_profile, kind='linear', bounds_error=False, fill_value=0.0) 109 | 110 | # Calculate the range to each pixel 111 | range_image = x_image * cos(el) * cos(az) + y_image * cos(el) * sin(az) + z_image * sin(el) 112 | 113 | # Interpolate the range profile onto the image grid and multiply by the range phase 114 | # For large scenes, should check the range window and index 115 | bp_image += f(range_image) * exp(term * range_image) 116 | 117 | index += 1 118 | 119 | return bp_image 120 | 121 | 122 | def reconstruct3(signal, az, el, x_image, y_image, z_image, frequency, fft_length): 123 | """ 124 | Reconstruct the three-dimensional image using the filtered backprojection method. 125 | :param signal: The signal in K-space. 126 | :param az: The sensor azimuth positions (rad). 127 | :param el: The sensor elevation positions (rad). 128 | :param x_image: The image x-coordinates (m). 129 | :param y_image: The image y-coordinates (m). 130 | :param z_image: The image z-coordinates (m). 131 | :param frequency: The frequency array (Hz). 132 | :param fft_length: The number of points in the FFT. 133 | :return: The reconstructed image. 134 | """ 135 | # Get the frequency step size 136 | frequency_step = frequency[1] - frequency[0] 137 | 138 | # Calculate the maximum scene size and resolution 139 | range_extent = c / (2.0 * frequency_step) 140 | 141 | # Calculate the range window for the pulses 142 | range_window = linspace(-0.5 * range_extent, 0.5 * range_extent, fft_length) 143 | 144 | # Initialize the image 145 | bp_image = zeros_like(x_image, dtype=complex) 146 | 147 | # Loop over all pulses in the data 148 | term = 1j * 4.0 * pi * frequency[0] / c 149 | 150 | # Number of rows and columns 151 | nr, nc = az.shape 152 | 153 | for row in range(nr): 154 | for col in range(nc): 155 | 156 | # Calculate the range profile 157 | range_profile = fftshift(ifft(signal[:, row, col], fft_length)) 158 | 159 | # Create the interpolation for this pulse 160 | f = interp1d(range_window, range_profile, kind='linear', bounds_error=False, fill_value=0.0) 161 | 162 | # Calculate the range to each pixel 163 | range_image = x_image * cos(el[row, col]) * cos(az[row, col]) + \ 164 | y_image * cos(el[row, col]) * sin(az[row, col]) + \ 165 | z_image * sin(el[row, col]) 166 | 167 | # Interpolate the range profile onto the image grid and multiply by the range phase 168 | # For large scenes, should check the range window and index 169 | bp_image += f(range_image) * exp(term * range_image) 170 | 171 | return bp_image 172 | -------------------------------------------------------------------------------- /python/Chapter3/signal.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/python/Chapter3/signal.mat -------------------------------------------------------------------------------- /python/Chapter4/backprojection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Project: RadarBook 3 | File: backprojection.py 4 | Created by: Lee A. Harrison 5 | One: 2/9/2019 6 | Created with: PyCharm 7 | 8 | Copyright (C) 2022 Artech House (artech@artechhouse.com) 9 | This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 10 | and can not be copied and/or distributed without the express permission of Artech House. 11 | """ 12 | from scipy.constants import c, pi 13 | from numpy import sqrt, linspace, zeros_like, exp, sin, cos, ones 14 | from scipy.interpolate import interp1d 15 | from scipy.fftpack import ifft, fftshift 16 | 17 | 18 | def reconstruct(signal, sensor_x, sensor_y, sensor_z, range_center, x_image, y_image, z_image, frequency, fft_length): 19 | """ 20 | Reconstruct the two-dimensional image using the filtered backprojection method. 21 | :param signal: The signal in K-space. 22 | :param sensor_x: The sensor x-coordinate (m). 23 | :param sensor_y: The sensor y-coordinate (m). 24 | :param sensor_z: The sensor z-coordinate (m). 25 | :param range_center: The range to the center of the image (m). 26 | :param x_image: The x-coordinates of the image (m). 27 | :param y_image: The y-coordinates of the image (m). 28 | :param z_image: The z-coordinates of the image (m). 29 | :param frequency: The frequency array (Hz). 30 | :param fft_length: The number of points in the FFT. 31 | :return: The reconstructed image. 32 | """ 33 | # Get the frequency step size 34 | frequency_step = frequency[1] - frequency[0] 35 | 36 | # Calculate the maximum scene size and resolution 37 | range_extent = c / (2.0 * frequency_step) 38 | 39 | # Calculate the range window for the pulses 40 | range_window = linspace(-0.5 * range_extent, 0.5 * range_extent, fft_length) 41 | 42 | # Initialize the image 43 | bp_image = zeros_like(x_image, dtype=complex) 44 | 45 | # Loop over all pulses in the data 46 | term = 1j * 4.0 * pi * frequency[0] / c 47 | 48 | # To work with stripmap 49 | if not isinstance(range_center, list): 50 | range_center *= ones(len(sensor_x)) 51 | 52 | index = 0 53 | for xs, ys, zs in zip(sensor_x, sensor_y, sensor_z): 54 | 55 | # Calculate the range profile 56 | range_profile = fftshift(ifft(signal[:, index], fft_length)) 57 | 58 | # Create the interpolation for this pulse 59 | f = interp1d(range_window, range_profile, kind='linear', bounds_error=False, fill_value=0.0) 60 | 61 | # Calculate the range to each pixel 62 | range_image = sqrt((xs - x_image) ** 2 + (ys - y_image) ** 2 + (zs - z_image) ** 2) - range_center[index] 63 | 64 | # Interpolate the range profile onto the image grid and multiply by the range phase 65 | # For large scenes, should check the range window and index 66 | bp_image += f(range_image) * exp(term * range_image) 67 | 68 | index += 1 69 | 70 | return bp_image 71 | 72 | 73 | def reconstruct2(signal, sensor_az, sensor_el, x_image, y_image, z_image, frequency, fft_length): 74 | """ 75 | Reconstruct the two-dimensional image using the filtered backprojection method. 76 | :param signal: The signal in K-space. 77 | :param sensor_az: The sensor azimuth positions (rad). 78 | :param sensor_el: The sensor elevation positions (rad). 79 | :param x_image: The image x-coordinates (m). 80 | :param y_image: The image y-coordinates (m). 81 | :param z_image: The image z-coordinates (m). 82 | :param frequency: The frequency array (Hz). 83 | :param fft_length: The number of points in the FFT. 84 | :return: The reconstructed image. 85 | """ 86 | # Get the frequency step size 87 | frequency_step = frequency[1] - frequency[0] 88 | 89 | # Calculate the maximum scene size and resolution 90 | range_extent = c / (2.0 * frequency_step) 91 | 92 | # Calculate the range window for the pulses 93 | range_window = linspace(-0.5 * range_extent, 0.5 * range_extent, fft_length) 94 | 95 | # Initialize the image 96 | bp_image = zeros_like(x_image, dtype=complex) 97 | 98 | # Loop over all pulses in the data 99 | term = 1j * 4.0 * pi * frequency[0] / c 100 | 101 | index = 0 102 | for az, el in zip(sensor_az, sensor_el): 103 | 104 | # Calculate the range profile 105 | range_profile = fftshift(ifft(signal[:, index], fft_length)) 106 | 107 | # Create the interpolation for this pulse 108 | f = interp1d(range_window, range_profile, kind='linear', bounds_error=False, fill_value=0.0) 109 | 110 | # Calculate the range to each pixel 111 | range_image = x_image * cos(el) * cos(az) + y_image * cos(el) * sin(az) + z_image * sin(el) 112 | 113 | # Interpolate the range profile onto the image grid and multiply by the range phase 114 | # For large scenes, should check the range window and index 115 | bp_image += f(range_image) * exp(term * range_image) 116 | 117 | index += 1 118 | 119 | return bp_image 120 | 121 | 122 | def reconstruct3(signal, az, el, x_image, y_image, z_image, frequency, fft_length): 123 | """ 124 | Reconstruct the three-dimensional image using the filtered backprojection method. 125 | :param signal: The signal in K-space. 126 | :param az: The sensor azimuth positions (rad). 127 | :param el: The sensor elevation positions (rad). 128 | :param x_image: The image x-coordinates (m). 129 | :param y_image: The image y-coordinates (m). 130 | :param z_image: The image z-coordinates (m). 131 | :param frequency: The frequency array (Hz). 132 | :param fft_length: The number of points in the FFT. 133 | :return: The reconstructed image. 134 | """ 135 | # Get the frequency step size 136 | frequency_step = frequency[1] - frequency[0] 137 | 138 | # Calculate the maximum scene size and resolution 139 | range_extent = c / (2.0 * frequency_step) 140 | 141 | # Calculate the range window for the pulses 142 | range_window = linspace(-0.5 * range_extent, 0.5 * range_extent, fft_length) 143 | 144 | # Initialize the image 145 | bp_image = zeros_like(x_image, dtype=complex) 146 | 147 | # Loop over all pulses in the data 148 | term = 1j * 4.0 * pi * frequency[0] / c 149 | 150 | # Number of rows and columns 151 | nr, nc = az.shape 152 | 153 | for row in range(nr): 154 | for col in range(nc): 155 | 156 | # Calculate the range profile 157 | range_profile = fftshift(ifft(signal[:, row, col], fft_length)) 158 | 159 | # Create the interpolation for this pulse 160 | f = interp1d(range_window, range_profile, kind='linear', bounds_error=False, fill_value=0.0) 161 | 162 | # Calculate the range to each pixel 163 | range_image = x_image * cos(el[row, col]) * cos(az[row, col]) + \ 164 | y_image * cos(el[row, col]) * sin(az[row, col]) + \ 165 | z_image * sin(el[row, col]) 166 | 167 | # Interpolate the range profile onto the image grid and multiply by the range phase 168 | # For large scenes, should check the range window and index 169 | bp_image += f(range_image) * exp(term * range_image) 170 | 171 | return bp_image 172 | -------------------------------------------------------------------------------- /python/Chapter5/phase_gradient.py: -------------------------------------------------------------------------------- 1 | """ 2 | Project: SAR Book 3 | File: pga.py 4 | Created by: Lee A. Harrison 5 | On: 2/9/2022 6 | Created with: PyCharm 7 | 8 | Copyright (C) 2022 Artech House (artech@artechhouse.com) 9 | This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 10 | and can not be copied and/or distributed without the express permission of Artech House. 11 | """ 12 | import numpy as np 13 | # from scipy.constants import c, pi 14 | # from numpy import sqrt, linspace, zeros_like, exp, sin, cos, ones 15 | # from scipy.interpolate import interp1d 16 | # from scipy.fftpack import ifft, fftshift 17 | 18 | from matplotlib import pyplot as plt 19 | 20 | 21 | def pga(original_image, threshold): 22 | """ 23 | Peform phsae gradient autofocus. 24 | :param original_image: The unfocused image. 25 | :param sensor_x: The threshold for setting the window width (dB). 26 | :return: The corrected image and the phase error estimate. 27 | """ 28 | save_image = original_image.copy() 29 | 30 | index_center = int(np.ceil(original_image.shape[1] / 2)) 31 | 32 | 33 | # Find the index of each max 34 | maximum_along_az_idx = [] 35 | 36 | for i in range(original_image.shape[0]): 37 | maximum_along_az_idx.append(original_image[i,:].argmax()) 38 | 39 | 40 | # Circular shift 41 | for i in range(original_image.shape[0]): 42 | original_image[i,:] = np.roll(original_image[i,:], int(index_center - maximum_along_az_idx[i])) 43 | 44 | 45 | 46 | # Calculate the window width 47 | noncoherent_window = np.sum(abs(original_image)**2, 0) 48 | 49 | window_cutoff = np.max(20 * np.log10(noncoherent_window)) - abs(threshold) 50 | 51 | 52 | 53 | # Find the indices for the window 54 | last_index = np.argwhere(20 * np.log10(noncoherent_window[0:index_center]) - window_cutoff<0) 55 | 56 | last_index = last_index[-1] 57 | 58 | 59 | first_index = np.argwhere(20 * np.log10(noncoherent_window[index_center+1:-1]) - window_cutoff<0) 60 | 61 | first_index = first_index[0] 62 | 63 | first_index = first_index + index_center - 1 64 | 65 | 66 | # Get the window filter 67 | noncoherent_window = np.zeros_like(noncoherent_window) 68 | 69 | noncoherent_window[last_index[0]:first_index[0]] = 1 70 | 71 | 72 | # Set the FFT size 73 | nfft = original_image.shape[1] 74 | 75 | # Calculate the image spectrum 76 | term1 = original_image * np.matlib.repmat(noncoherent_window, original_image.shape[0], 1) 77 | 78 | image_spectrum = np.fft.fft(np.fft.ifftshift(term1, 1), nfft, axis=1) 79 | 80 | 81 | # Calculate the delta phase and the error estimate 82 | delta_phase = np.angle( np.sum( np.conj(image_spectrum[:, 0:-2]) * image_spectrum[:,1:-1], axis=0 )) 83 | 84 | phase_error_estimate = np.zeros(nfft) 85 | 86 | phase_error_estimate[0:len(delta_phase)] = np.cumsum(delta_phase) 87 | 88 | 89 | # Calculate the linear phase term 90 | linear_coefs = np.polyfit(range(len(phase_error_estimate)), phase_error_estimate, 1) 91 | 92 | # Updated phase error estimate 93 | phase_error_estimate = np.unwrap(phase_error_estimate - np.polyval(linear_coefs, range(len(phase_error_estimate)))) 94 | 95 | # Calculate the image spectrum 96 | image_spectrum = np.fft.fft(np.fft.ifftshift(save_image, 1), nfft, axis=1) 97 | 98 | # Correct image spectrum with phase estimate 99 | image_spectrum = image_spectrum * np.matlib.repmat(np.exp(-1j*phase_error_estimate), save_image.shape[0], 1) 100 | 101 | # Finally compute the focused image 102 | corrected_image = np.fft.fftshift(np.fft.ifft(image_spectrum, axis=1), 1) 103 | 104 | return corrected_image, phase_error_estimate -------------------------------------------------------------------------------- /python/Chapter6/ICEYE_Harris_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/python/Chapter6/ICEYE_Harris_1.jpg -------------------------------------------------------------------------------- /python/Chapter6/ICEYE_Harris_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/python/Chapter6/ICEYE_Harris_2.jpg -------------------------------------------------------------------------------- /python/Chapter6/ICEYE_ORB.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/python/Chapter6/ICEYE_ORB.jpg -------------------------------------------------------------------------------- /python/Chapter6/ICEYE_Phase_Correlation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/python/Chapter6/ICEYE_Phase_Correlation.jpg -------------------------------------------------------------------------------- /python/Chapter6/ICEYE_SIFT.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SARBook/software/77d77c627c03206a566889de78bd58bba851cc28/python/Chapter6/ICEYE_SIFT.jpg -------------------------------------------------------------------------------- /python/Chapter6/harris_corner_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction to Synthetic Aperture Radar Using Python and MATLAB\n", 8 | "\n", 9 | "## by Andy Harrison - © Artech House 2022\n", 10 | "\n", 11 | "---\n", 12 | "\n", 13 | "## Example 6.8.1 Harris Corner\n", 14 | "---" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "**Import modules**" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "import cv2\n", 31 | "\n", 32 | "import numpy as np" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "**Helper function for displaying images**" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "def imshow(image, windowName, nx, ny):\n", 49 | " cv2.namedWindow(windowName, cv2.WINDOW_NORMAL) # Create a new named window\n", 50 | " cv2.moveWindow(windowName, 0, 0) # Put window @ (0, 0)\n", 51 | " cv2.imshow(windowName, image) # Display the image\n", 52 | " cv2.resizeWindow(windowName, nx, ny) # Resize the window" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "**Read the images**" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 3, 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "image1 = cv2.imread('ICEYE_Harris_1.jpg')\n", 69 | "\n", 70 | "image2 = cv2.imread('ICEYE_Harris_2.jpg')" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "**Convert to grayscale**" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 4, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "image2_gray = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY).astype(np.float32)\n", 87 | "\n", 88 | "image1_gray = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY).astype(np.float32)" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "**Harris corners**" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 5, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "distance1 = cv2.cornerHarris(image1_gray, 2, 3, 0.04)\n", 105 | "\n", 106 | "distance2 = cv2.cornerHarris(image2_gray, 2, 3, 0.04)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "**Dilate for marking corners**" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 6, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "distance1 = cv2.dilate(distance1, None)\n", 123 | "\n", 124 | "distance2 = cv2.dilate(distance2, None)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "metadata": {}, 130 | "source": [ 131 | "**Set the threshold, it may vary depending on the image**" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 7, 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [ 140 | "image2[distance1 > 0.1 * distance1.max()] = [0, 0, 255]\n", 141 | "\n", 142 | "image1[distance2 > 0.1 * distance2.max()] = [0, 0, 255]" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "**Display the images**" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "imshow(image1, 'Harris Corner Image 1', 600, 600)\n", 159 | "\n", 160 | "imshow(image2, 'Harris Corner Image 2', 600, 600)\n", 161 | "\n", 162 | "cv2.waitKey()" 163 | ] 164 | } 165 | ], 166 | "metadata": { 167 | "kernelspec": { 168 | "display_name": "Python 3", 169 | "language": "python", 170 | "name": "python3" 171 | }, 172 | "language_info": { 173 | "codemirror_mode": { 174 | "name": "ipython", 175 | "version": 3 176 | }, 177 | "file_extension": ".py", 178 | "mimetype": "text/x-python", 179 | "name": "python", 180 | "nbconvert_exporter": "python", 181 | "pygments_lexer": "ipython3", 182 | "version": "3.8.5" 183 | } 184 | }, 185 | "nbformat": 4, 186 | "nbformat_minor": 4 187 | } 188 | -------------------------------------------------------------------------------- /python/Chapter6/orb_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction to Synthetic Aperture Radar Using Python and MATLAB\n", 8 | "\n", 9 | "## by Andy Harrison - © Artech House 2022\n", 10 | "\n", 11 | "---\n", 12 | "\n", 13 | "## Example 6.8.4 ORB\n", 14 | "---" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "**Import modules**" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "import cv2\n", 31 | "\n", 32 | "import numpy as np" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "**Helper function for displaying images**" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "def imshow(image, windowName, nx, ny):\n", 49 | " cv2.namedWindow(windowName, cv2.WINDOW_NORMAL) # Create a new named window\n", 50 | " cv2.moveWindow(windowName, 0, 0) # Put window @ (0, 0)\n", 51 | " cv2.imshow(windowName, image) # Display the image\n", 52 | " cv2.resizeWindow(windowName, nx, ny) # Resize the window" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "**Read the original image**" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 3, 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "image1 = cv2.imread('ICEYE_ORB.jpg')" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "**Define the source and destination triangles**" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 4, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "srcTri = np.array( [[0, 0], [image1.shape[1] - 1, 0], [0, image1.shape[0] - 1]] ).astype(np.float32)\n", 85 | "\n", 86 | "dstTri = np.array( [[0, image1.shape[1]*0.1], [image1.shape[1]*0.78, image1.shape[0]*0.1], [image1.shape[1]*0.2, image1.shape[0]*0.85]] ).astype(np.float32)" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "**Get the affine transform**" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 5, 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "warp_mat = cv2.getAffineTransform(srcTri, dstTri)\n", 103 | "\n", 104 | "warp_dst = cv2.warpAffine(image1, warp_mat, (image1.shape[1], image1.shape[0]))" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "**Rotating the image after Warp**" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 6, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "center = (warp_dst.shape[1] // 2, warp_dst.shape[0] // 2)\n", 121 | "\n", 122 | "angle = 17\n", 123 | "\n", 124 | "scale = 0.86\n", 125 | "\n", 126 | "rot_mat = cv2.getRotationMatrix2D(center, angle, scale)\n", 127 | "\n", 128 | "warp_rotate_dst = cv2.warpAffine(warp_dst, rot_mat, (warp_dst.shape[1], warp_dst.shape[0]))" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "**Display the original and warped images**" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 7, 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [ 144 | "imshow(image1, 'Source Image', 600, 600)\n", 145 | "\n", 146 | "imshow(warp_rotate_dst, 'Warped Image', 600, 600)\n", 147 | "\n", 148 | "image1s = image1" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "metadata": {}, 154 | "source": [ 155 | "**Convert images to grayscale**" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": 8, 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "image2 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)\n", 165 | "\n", 166 | "image1 = cv2.cvtColor(warp_rotate_dst, cv2.COLOR_BGR2GRAY)\n", 167 | "\n", 168 | "\n", 169 | "# ORB\n", 170 | "\n", 171 | "orb = cv2.ORB_create()" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": {}, 177 | "source": [ 178 | "**Find the keypoints with ORB**" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": 9, 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [ 187 | "kp1 = orb.detect(image1, None)\n", 188 | "\n", 189 | "kp2 = orb.detect(image2, None)" 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": {}, 195 | "source": [ 196 | "**compute the descriptors with ORB**" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 10, 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "keypoints_1, descriptors_1 = orb.compute(image1, kp1)\n", 206 | "\n", 207 | "keypoints_2, descriptors_2 = orb.compute(image2, kp2)" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": {}, 213 | "source": [ 214 | "**Find feature matches**" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 11, 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [ 223 | "bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=True)\n", 224 | "\n", 225 | "matches = bf.match(descriptors_1, descriptors_2)\n", 226 | "\n", 227 | "matches = sorted(matches, key=lambda x: x.distance)" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "**Display the matches**" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": 12, 240 | "metadata": {}, 241 | "outputs": [], 242 | "source": [ 243 | "image3 = cv2.drawMatches(warp_rotate_dst, keypoints_1, image1s, keypoints_2, matches[:20], image2, flags=2)\n", 244 | "\n", 245 | "imshow(image3, 'Matches', 600, 600)" 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": {}, 251 | "source": [ 252 | "**Get the coordinates of the keypoints**" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 13, 258 | "metadata": {}, 259 | "outputs": [], 260 | "source": [ 261 | "list_kp1 = np.float32([keypoints_1[mat.queryIdx].pt for mat in matches]).reshape(-1, 1, 2)\n", 262 | "\n", 263 | "list_kp2 = np.float32([keypoints_2[mat.trainIdx].pt for mat in matches]).reshape(-1, 1, 2)" 264 | ] 265 | }, 266 | { 267 | "cell_type": "markdown", 268 | "metadata": {}, 269 | "source": [ 270 | "**With coordinates of keypoints, find homography**" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": 14, 276 | "metadata": {}, 277 | "outputs": [], 278 | "source": [ 279 | "h, status = cv2.findHomography(list_kp1[0:5], list_kp2[0:5], cv2.RANSAC, 5.0)" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "**Warp source image to destination based on homography**" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": null, 292 | "metadata": {}, 293 | "outputs": [], 294 | "source": [ 295 | "image3 = cv2.warpPerspective(warp_rotate_dst, h, (image2.shape[1], image2.shape[0]))\n", 296 | "\n", 297 | "imshow(image3, 'Final Image', 600, 600)\n", 298 | "\n", 299 | "cv2.waitKey()" 300 | ] 301 | } 302 | ], 303 | "metadata": { 304 | "kernelspec": { 305 | "display_name": "Python 3", 306 | "language": "python", 307 | "name": "python3" 308 | }, 309 | "language_info": { 310 | "codemirror_mode": { 311 | "name": "ipython", 312 | "version": 3 313 | }, 314 | "file_extension": ".py", 315 | "mimetype": "text/x-python", 316 | "name": "python", 317 | "nbconvert_exporter": "python", 318 | "pygments_lexer": "ipython3", 319 | "version": "3.8.5" 320 | } 321 | }, 322 | "nbformat": 4, 323 | "nbformat_minor": 4 324 | } 325 | -------------------------------------------------------------------------------- /python/Chapter6/phase_correlation_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction to Synthetic Aperture Radar Using Python and MATLAB\n", 8 | "\n", 9 | "## by Andy Harrison - © Artech House 2022\n", 10 | "\n", 11 | "---\n", 12 | "\n", 13 | "## Example 6.8.2 Phase Correlation\n", 14 | "---" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "**Import modules**" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "import cv2\n", 31 | "\n", 32 | "import numpy as np\n", 33 | "\n", 34 | "from scipy.signal import cheby1, freqs\n", 35 | "\n", 36 | "from scipy.ndimage import map_coordinates" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "**Helper function for displaying images**" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 2, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "def imshow(image, windowName, nx, ny):\n", 53 | " cv2.namedWindow(windowName, cv2.WINDOW_NORMAL) # Create a new named window\n", 54 | " cv2.moveWindow(windowName, 0, 0) # Put window @ (0, 0)\n", 55 | " cv2.imshow(windowName, image) # Display the image\n", 56 | " cv2.resizeWindow(windowName, nx, ny) # Resize the window" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "**Log polar converstion**" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 3, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "def log_polar(image):\n", 73 | " \"\"\"\n", 74 | " Calculate the log-polar conversion\n", 75 | " \"\"\"\n", 76 | " # Image dimensions\n", 77 | " nx, ny = image.shape\n", 78 | " \n", 79 | "\n", 80 | " # Get the angles using meshgrid\n", 81 | " _, theta = np.meshgrid(np.linspace(0, 1, ny), np.linspace(0, -np.pi, nx))\n", 82 | " \n", 83 | "\n", 84 | " # Calculate the log base\n", 85 | " log_base = 10.0 ** (np.log10(np.sqrt((0.5 * nx) ** 2 + (0.5 * ny) ** 2)) / ny)\n", 86 | " \n", 87 | "\n", 88 | " # Calculate the radius based on the log base\n", 89 | " radius = log_base ** np.arange(ny) - 1.0\n", 90 | " \n", 91 | "\n", 92 | " # Calculate the x and y coordinates\n", 93 | " x = radius * np.sin(theta) + nx / 2\n", 94 | " y = radius * np.cos(theta) + ny / 2\n", 95 | " \n", 96 | "\n", 97 | " # Map the image onto the coordinates\n", 98 | " return log_base, map_coordinates(image, [x, y])" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "**Perform scale, translation, and rotation of an image**" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": 4, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "def scale_translate_rotate(image, scale_x, scale_y, translate_x, translate_y, rotation):\n", 115 | " \"\"\"\n", 116 | " Scale, rotate, and translate an image\n", 117 | " \"\"\"\n", 118 | " height, width = image.shape[:2]\n", 119 | "\n", 120 | " nh = int(np.round(1.2*height))\n", 121 | " \n", 122 | " nw = int(np.round(1.2*width))\n", 123 | "\n", 124 | "\n", 125 | " # Scale\n", 126 | " # image = cv2.resize(image, None, fx=scale_x, fy=scale_y, interpolation=cv2.INTER_CUBIC)\n", 127 | " image = cv2.warpAffine(image, np.float32([[scale_x, 0, 0], [0, scale_y, 0]]), (nw, nh))\n", 128 | " \n", 129 | "\n", 130 | " # Translation\n", 131 | " m = np.float32([[1, 0, translate_x], [0, 1, translate_y]])\n", 132 | " image = cv2.warpAffine(image, m, (nw, nh))\n", 133 | " \n", 134 | "\n", 135 | " # Rotation\n", 136 | " m = cv2.getRotationMatrix2D(((width - 1) / 2.0, (height - 1) / 2.0), rotation, 1)\n", 137 | " image = cv2.warpAffine(image, m, (nw, nh))\n", 138 | " \n", 139 | "\n", 140 | " return image" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "**Read an image**" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 5, 153 | "metadata": {}, 154 | "outputs": [], 155 | "source": [ 156 | "image1 = cv2.imread('ICEYE_Phase_Correlation.jpg')" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "**Apply a scale, translation, and rotation**" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 6, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "image2 = scale_translate_rotate(image1, 0.6, 0.6, 200, 100, 30)" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "**Begin phase correlation - convert to grayscale**" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 7, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)\n", 189 | "\n", 190 | "image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "**Display both images**" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": 8, 203 | "metadata": {}, 204 | "outputs": [], 205 | "source": [ 206 | "imshow(image1, 'Original Image', 600, 600)\n", 207 | "\n", 208 | "imshow(image2, 'Scaled, Translated, Rotated', 600, 600)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "**Find the log-polar transformed image and the log base**" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": 9, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [ 224 | "f1 = np.fft.fftshift(abs(np.fft.fft2(image1, image2.shape[:2])))\n", 225 | "\n", 226 | "f2 = np.fft.fftshift(abs(np.fft.fft2(image2, image2.shape[:2])))" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": {}, 232 | "source": [ 233 | "**2nd order Chebyshev bandstop filter**" 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": 10, 239 | "metadata": {}, 240 | "outputs": [], 241 | "source": [ 242 | "[b, a] = cheby1(2, 0.1, [0.01, 0.5], 'bandstop', analog=True)\n", 243 | "\n", 244 | "w, h1 = freqs(b, a, np.linspace(-1, 1, f2.shape[0]))\n", 245 | "\n", 246 | "w, h2 = freqs(b, a, np.linspace(-1, 1, f2.shape[1]))" 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "**Create and apply the 2D filter**" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 11, 259 | "metadata": {}, 260 | "outputs": [], 261 | "source": [ 262 | "h = np.outer(1 - abs(h1), 1 - abs(h2))\n", 263 | "\n", 264 | "f1 *= (1 - h)\n", 265 | "\n", 266 | "f2 *= (1 - h)" 267 | ] 268 | }, 269 | { 270 | "cell_type": "markdown", 271 | "metadata": {}, 272 | "source": [ 273 | "**Find the log-polar transformed image and the log base**" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": 12, 279 | "metadata": {}, 280 | "outputs": [], 281 | "source": [ 282 | "base, log_polar_original = log_polar(f1)\n", 283 | "\n", 284 | "base, log_polar_test = log_polar(f2)" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": {}, 290 | "source": [ 291 | "**Calculate the cross-power spectrum of the original and test images**" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 13, 297 | "metadata": {}, 298 | "outputs": [], 299 | "source": [ 300 | "f1 = np.fft.fft2(log_polar_original)\n", 301 | "\n", 302 | "f2 = np.fft.fft2(log_polar_test)\n", 303 | "\n", 304 | "r0 = abs(f1) * abs(f2)\n", 305 | "\n", 306 | "cross_power_spectrum = abs(np.fft.ifft2((f1 * f2.conjugate()) / r0))" 307 | ] 308 | }, 309 | { 310 | "cell_type": "markdown", 311 | "metadata": {}, 312 | "source": [ 313 | "**Calculate the scale and rotation factors**" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": 14, 319 | "metadata": {}, 320 | "outputs": [], 321 | "source": [ 322 | "angle, scale = np.unravel_index(np.argmax(cross_power_spectrum), cross_power_spectrum.shape)\n", 323 | "\n", 324 | "\n", 325 | "# Calculate the scale factor\n", 326 | "scale = base ** scale\n", 327 | "\n", 328 | "\n", 329 | "# Calculate the rotation angle\n", 330 | "angle = 180.0 * angle / cross_power_spectrum.shape[0]\n", 331 | "\n", 332 | "\n", 333 | "# Adjust the scale factor and rotation angle\n", 334 | "if scale > 1.8:\n", 335 | " cross_power_spectrum = abs(np.fft.ifft2((f2 * f1.conjugate()) / r0))\n", 336 | " \n", 337 | " angle, scale = np.unravel_index(np.argmax(cross_power_spectrum), cross_power_spectrum.shape)\n", 338 | " \n", 339 | " \n", 340 | " angle = -180.0 * angle / cross_power_spectrum.shape[0]\n", 341 | " scale = 1.0 / (base ** scale)\n", 342 | " \n", 343 | "\n", 344 | "# Wrap the rotation angle (-90, 90]\n", 345 | "angle = (angle + 90) % 180 - 90" 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": {}, 351 | "source": [ 352 | "**Zoom the test image using the scale factor**" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": 15, 358 | "metadata": {}, 359 | "outputs": [], 360 | "source": [ 361 | "image_transformed = scale_translate_rotate(image2, 1, 1, 0, 0, angle)\n", 362 | "\n", 363 | "image_transformed = scale_translate_rotate(image2, 1./scale, 1./scale, 0, 0, 0)" 364 | ] 365 | }, 366 | { 367 | "cell_type": "markdown", 368 | "metadata": {}, 369 | "source": [ 370 | "**Need to handle image shapes for rotated and zoomed images**" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": 16, 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "nxo, nyo = image1.shape\n", 380 | "\n", 381 | "nxt, nyt = image_transformed.shape\n", 382 | "\n", 383 | "image_transformed = image_transformed[int((nxt - nxo) / 2):int((nxt + nxo) / 2), \n", 384 | " int((nyt - nyo) / 2):int((nyt + nyo) / 2)]" 385 | ] 386 | }, 387 | { 388 | "cell_type": "markdown", 389 | "metadata": {}, 390 | "source": [ 391 | "**Calculate the cross-power spectrum of the original and transformed images**" 392 | ] 393 | }, 394 | { 395 | "cell_type": "code", 396 | "execution_count": 17, 397 | "metadata": {}, 398 | "outputs": [], 399 | "source": [ 400 | "image_original_spectrum = np.fft.fft2(image1)\n", 401 | "\n", 402 | "image_transformed_spectrum = np.fft.fft2(image_transformed)\n", 403 | "\n", 404 | "translation = abs(np.fft.ifft2((image_original_spectrum * image_transformed_spectrum.conjugate()) / \n", 405 | " (abs(image_original_spectrum) * abs(image_transformed_spectrum))))" 406 | ] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "metadata": {}, 411 | "source": [ 412 | "**Find the translation vector**" 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": 18, 418 | "metadata": {}, 419 | "outputs": [], 420 | "source": [ 421 | "translate_x, translate_y = np.unravel_index(np.argmax(translation), translation.shape)" 422 | ] 423 | }, 424 | { 425 | "cell_type": "markdown", 426 | "metadata": {}, 427 | "source": [ 428 | "**Force translation vector in the range of image indices**" 429 | ] 430 | }, 431 | { 432 | "cell_type": "code", 433 | "execution_count": 19, 434 | "metadata": {}, 435 | "outputs": [], 436 | "source": [ 437 | "if translate_x > np.floor(nxo / 2):\n", 438 | " translate_x -= nxo\n", 439 | "\n", 440 | "if translate_y > np.floor(nyo / 2):\n", 441 | " translate_y -= nyo" 442 | ] 443 | }, 444 | { 445 | "cell_type": "markdown", 446 | "metadata": {}, 447 | "source": [ 448 | "**Translate the transformed imaged**" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 20, 454 | "metadata": {}, 455 | "outputs": [], 456 | "source": [ 457 | "image_transformed = scale_translate_rotate(image_transformed, 1, 1, translate_x, translate_y, 0)" 458 | ] 459 | }, 460 | { 461 | "cell_type": "markdown", 462 | "metadata": {}, 463 | "source": [ 464 | "**Correct the translation vector based on the rotation angle**" 465 | ] 466 | }, 467 | { 468 | "cell_type": "code", 469 | "execution_count": 21, 470 | "metadata": {}, 471 | "outputs": [], 472 | "source": [ 473 | "if angle > 0.0:\n", 474 | " offset = int(int(image2.shape[1] / scale) * np.sin(np.radians(angle)))\n", 475 | " translate_x, translate_y = translate_y, offset + translate_x\n", 476 | "\n", 477 | "elif angle < 0.0:\n", 478 | " offset = int(int(image2.shape[0] / scale) * np.sin(np.radians(angle)))\n", 479 | " translate_x, translate_y = offset + translate_y, offset + translate_x" 480 | ] 481 | }, 482 | { 483 | "cell_type": "markdown", 484 | "metadata": {}, 485 | "source": [ 486 | "**Correct the scale factor**" 487 | ] 488 | }, 489 | { 490 | "cell_type": "code", 491 | "execution_count": 22, 492 | "metadata": {}, 493 | "outputs": [], 494 | "source": [ 495 | "scale = (image2.shape[1] - 1) / (int(image2.shape[1] / scale) - 1)" 496 | ] 497 | }, 498 | { 499 | "cell_type": "markdown", 500 | "metadata": {}, 501 | "source": [ 502 | "**Transform the image and display the results**" 503 | ] 504 | }, 505 | { 506 | "cell_type": "code", 507 | "execution_count": 23, 508 | "metadata": {}, 509 | "outputs": [ 510 | { 511 | "data": { 512 | "text/plain": [ 513 | "13" 514 | ] 515 | }, 516 | "execution_count": 23, 517 | "metadata": {}, 518 | "output_type": "execute_result" 519 | } 520 | ], 521 | "source": [ 522 | "image_transformed = scale_translate_rotate(image2, 1./scale, 1./scale, -20, -10, angle)\n", 523 | "\n", 524 | "imshow(image_transformed, 'Transformed Image', 600, 600)\n", 525 | "\n", 526 | "cv2.waitKey()" 527 | ] 528 | } 529 | ], 530 | "metadata": { 531 | "kernelspec": { 532 | "display_name": "Python 3 (ipykernel)", 533 | "language": "python", 534 | "name": "python3" 535 | }, 536 | "language_info": { 537 | "codemirror_mode": { 538 | "name": "ipython", 539 | "version": 3 540 | }, 541 | "file_extension": ".py", 542 | "mimetype": "text/x-python", 543 | "name": "python", 544 | "nbconvert_exporter": "python", 545 | "pygments_lexer": "ipython3", 546 | "version": "3.10.1" 547 | } 548 | }, 549 | "nbformat": 4, 550 | "nbformat_minor": 4 551 | } 552 | -------------------------------------------------------------------------------- /python/Chapter6/sift_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction to Synthetic Aperture Radar Using Python and MATLAB\n", 8 | "\n", 9 | "## by Andy Harrison - © Artech House 2022\n", 10 | "\n", 11 | "---\n", 12 | "\n", 13 | "## Example 6.8.3 SIFT\n", 14 | "---" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "**Import modules**" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "import cv2\n", 31 | "\n", 32 | "import numpy as np" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "**Helper function for displaying images**" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "def imshow(image, windowName, nx, ny):\n", 49 | " cv2.namedWindow(windowName, cv2.WINDOW_NORMAL) # Create a new named window\n", 50 | " cv2.moveWindow(windowName, 0, 0) # Put window @ (0, 0)\n", 51 | " cv2.imshow(windowName, image) # Display the image\n", 52 | " cv2.resizeWindow(windowName, nx, ny) # Resize the window" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "**Read the original image**" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 3, 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "image1 = cv2.imread('ICEYE_SIFT.jpg')" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "**Define the source and destination triangles**" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 4, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "srcTri = np.array( [[0, 0], [image1.shape[1] - 1, 0], [0, image1.shape[0] - 1]] ).astype(np.float32)\n", 85 | "\n", 86 | "dstTri = np.array( [[0, image1.shape[1]*0.1], [image1.shape[1]*0.85, image1.shape[0]*0.1], [image1.shape[1]*0.2, image1.shape[0]*0.9]] ).astype(np.float32)" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "**Get the affine transform**" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 5, 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "warp_mat = cv2.getAffineTransform(srcTri, dstTri)\n", 103 | "\n", 104 | "warp_dst = cv2.warpAffine(image1, warp_mat, (image1.shape[1], image1.shape[0]))" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "**Rotating the image after Warp**" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 6, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "center = (warp_dst.shape[1] // 2, warp_dst.shape[0] // 2)\n", 121 | "\n", 122 | "angle = -30\n", 123 | "\n", 124 | "scale = 0.7\n", 125 | "\n", 126 | "rot_mat = cv2.getRotationMatrix2D(center, angle, scale)\n", 127 | "\n", 128 | "warp_rotate_dst = cv2.warpAffine(warp_dst, rot_mat, (warp_dst.shape[1], warp_dst.shape[0]))" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "**Display the original and warped images**" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 7, 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [ 144 | "imshow(image1, 'Source Image', 600, 600)\n", 145 | "\n", 146 | "imshow(warp_rotate_dst, 'Warped Image', 600, 600)\n", 147 | "\n", 148 | "image1s = image1" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "metadata": {}, 154 | "source": [ 155 | "**Convert the images to grayscale**" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": 8, 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "image2 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)\n", 165 | "\n", 166 | "image1 = cv2.cvtColor(warp_rotate_dst, cv2.COLOR_BGR2GRAY)\n", 167 | "\n", 168 | "# SIFT\n", 169 | "sift = cv2.SIFT_create()" 170 | ] 171 | }, 172 | { 173 | "cell_type": "markdown", 174 | "metadata": {}, 175 | "source": [ 176 | "**Detect and compute the keypoints and descriptors**" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 9, 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "keypoints_1, descriptors_1 = sift.detectAndCompute(image1, None)\n", 186 | "\n", 187 | "keypoints_2, descriptors_2 = sift.detectAndCompute(image2, None)" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "**Find the feature matches**" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 10, 200 | "metadata": {}, 201 | "outputs": [], 202 | "source": [ 203 | "bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=True)\n", 204 | "\n", 205 | "matches = bf.match(descriptors_1, descriptors_2)\n", 206 | "\n", 207 | "matches = sorted(matches, key=lambda x: x.distance)" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": {}, 213 | "source": [ 214 | "**Display the matches**" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 11, 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [ 223 | "image3 = cv2.drawMatches(warp_rotate_dst, keypoints_1, image1s, keypoints_2, matches[:20], image2, flags=2)\n", 224 | "\n", 225 | "imshow(image3, 'Matches', 600, 600)" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "**Get the coordinates of the keypoints**" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": 12, 238 | "metadata": {}, 239 | "outputs": [], 240 | "source": [ 241 | "list_kp1 = np.float32([keypoints_1[mat.queryIdx].pt for mat in matches]).reshape(-1, 1, 2)\n", 242 | "\n", 243 | "list_kp2 = np.float32([keypoints_2[mat.trainIdx].pt for mat in matches]).reshape(-1, 1, 2)" 244 | ] 245 | }, 246 | { 247 | "cell_type": "markdown", 248 | "metadata": {}, 249 | "source": [ 250 | "**With coordinates of keypoints, find homography**" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": 13, 256 | "metadata": {}, 257 | "outputs": [], 258 | "source": [ 259 | "h, status = cv2.findHomography(list_kp1[0:5], list_kp2[0:5], cv2.RANSAC, 5.0)" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": {}, 265 | "source": [ 266 | "**Warp source image to destination based on homography**" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": null, 272 | "metadata": {}, 273 | "outputs": [], 274 | "source": [ 275 | "image3 = cv2.warpPerspective(warp_rotate_dst, h, (image2.shape[1], image2.shape[0]))\n", 276 | "\n", 277 | "imshow(image3, 'Final Image', 600, 600)\n", 278 | "\n", 279 | "cv2.waitKey()" 280 | ] 281 | } 282 | ], 283 | "metadata": { 284 | "kernelspec": { 285 | "display_name": "Python 3", 286 | "language": "python", 287 | "name": "python3" 288 | }, 289 | "language_info": { 290 | "codemirror_mode": { 291 | "name": "ipython", 292 | "version": 3 293 | }, 294 | "file_extension": ".py", 295 | "mimetype": "text/x-python", 296 | "name": "python", 297 | "nbconvert_exporter": "python", 298 | "pygments_lexer": "ipython3", 299 | "version": "3.8.5" 300 | } 301 | }, 302 | "nbformat": 4, 303 | "nbformat_minor": 4 304 | } 305 | -------------------------------------------------------------------------------- /python/Chapter7/extended_range_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "89e2c921", 6 | "metadata": {}, 7 | "source": [ 8 | "# Introduction to Synthetic Aperture Radar Using Python and MATLAB\n", 9 | "\n", 10 | "## by Andy Harrison - © Artech House 2022\n", 11 | "\n", 12 | "---\n", 13 | "\n", 14 | "## Example 7.6.5 Unambiguous Range\n", 15 | "---" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "id": "624d3e7d", 21 | "metadata": {}, 22 | "source": [ 23 | "**Import modules**" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 1, 29 | "id": "c83d5833", 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "import numpy as np\n", 34 | "\n", 35 | "from scipy.constants import speed_of_light" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "id": "83200a7d", 41 | "metadata": {}, 42 | "source": [ 43 | "**Set the platform altitude (m)**" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 2, 49 | "id": "da064c54", 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "altitude = 500e3" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "id": "e6575ec4", 59 | "metadata": {}, 60 | "source": [ 61 | "**Set the PRF (Hz)**" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 3, 67 | "id": "dc8382a5", 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "prf = 1e3" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "id": "423be164", 77 | "metadata": {}, 78 | "source": [ 79 | "**Set the slant range to the scene (m)**" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 4, 85 | "id": "647c8264", 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "slant_range = 750e3" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "id": "d22384d5", 95 | "metadata": {}, 96 | "source": [ 97 | "**Calculate the number of pulses to extend the range to the target**" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 5, 103 | "id": "b3a8d689", 104 | "metadata": {}, 105 | "outputs": [ 106 | { 107 | "name": "stdout", 108 | "output_type": "stream", 109 | "text": [ 110 | "Number of pulses extended range 5\n" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "N = np.ceil(2 * slant_range / speed_of_light * prf - 1/2)\n", 116 | "\n", 117 | "print(f'Number of pulses extended range {int(N)}')" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "id": "9cc3609d", 123 | "metadata": {}, 124 | "source": [ 125 | "**Calculate the number of pulses to eclipse the nadir return**" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": 6, 131 | "id": "ef27374f", 132 | "metadata": {}, 133 | "outputs": [ 134 | { 135 | "name": "stdout", 136 | "output_type": "stream", 137 | "text": [ 138 | "Number of pulses nadir 4\n" 139 | ] 140 | } 141 | ], 142 | "source": [ 143 | "M = np.ceil((N + 1/2) * altitude / slant_range)\n", 144 | "\n", 145 | "print(f'Number of pulses nadir {int(M)}')" 146 | ] 147 | } 148 | ], 149 | "metadata": { 150 | "kernelspec": { 151 | "display_name": "Python 3 (ipykernel)", 152 | "language": "python", 153 | "name": "python3" 154 | }, 155 | "language_info": { 156 | "codemirror_mode": { 157 | "name": "ipython", 158 | "version": 3 159 | }, 160 | "file_extension": ".py", 161 | "mimetype": "text/x-python", 162 | "name": "python", 163 | "nbconvert_exporter": "python", 164 | "pygments_lexer": "ipython3", 165 | "version": "3.10.1" 166 | } 167 | }, 168 | "nbformat": 4, 169 | "nbformat_minor": 5 170 | } 171 | -------------------------------------------------------------------------------- /python/Chapter7/rain.py: -------------------------------------------------------------------------------- 1 | """ 2 | Project: SARRadarBook 3 | File: rain.py 4 | Created by: Lee A. Harrison 5 | On: 3/18/2018 6 | Created with: PyCharm 7 | 8 | Copyright (C) 2022 Artech House (artech@artechhouse.com) 9 | This file is part of Introduction to Synthetic Aperture Radar Using Python and MATLAB 10 | and can not be copied and/or distributed without the express permission of Artech House. 11 | """ 12 | from numpy import log10, exp, array, cos 13 | 14 | 15 | def attenuation(frequency, rain_rate, elevation_angle, polarization_tilt_angle): 16 | """ 17 | Calculate the attenuation due to rain. 18 | :param frequency: The operating frequency (GHz). 19 | :param rain_rate: The rain rate (mm/hr). 20 | :param elevation_angle: The elevation angle (radians). 21 | :param polarization_tilt_angle: The polarization tilt angle (radians). 22 | :return: The specific attenuation due to rain (dB/km). 23 | """ 24 | 25 | # Table 2.3 Coefficients for calculating k_h 26 | a_kh = array([-5.33980, -0.35351, -0.23789, -0.94158]) 27 | b_kh = array([-0.1008, 1.26970, 0.86036, 0.64552]) 28 | c_kh = array([1.13098, 0.45400, 0.15354, 0.16817]) 29 | d_kh = -0.18961 30 | e_kh = 0.71147 31 | 32 | # Table 2.4 Coefficients for calculating k_v 33 | a_kv = array([-3.80595, -3.44965, -0.39902, 0.50167]) 34 | b_kv = array([0.56934, -0.22911, 0.73042, 1.07319]) 35 | c_kv = array([0.81061, 0.51059, 0.11899, 0.27195]) 36 | d_kv = -0.16398 37 | e_kv = 0.63297 38 | 39 | # Table 2.5 Coefficients for calculating alpha_h 40 | a_ah = array([-0.14318, 0.29591, 0.32177, -5.37610, 16.1721]) 41 | b_ah = array([1.82442, 0.77564, 0.63773, -0.96230, -3.29980]) 42 | c_ah = array([-0.55187, 0.19822, 0.13164, 1.47828, 3.43990]) 43 | d_ah = 0.67849 44 | e_ah = -1.95537 45 | 46 | # Table 2.6 Coefficients for calculating alpha_v 47 | a_av = array([-0.07771, 0.56727, -0.20238, -48.2991, 48.5833]) 48 | b_av = array([2.33840, 0.95545, 1.14520, 0.791669, 0.791459]) 49 | c_av = array([-0.76284, 0.54039, 0.26809, 0.116226, 0.116479]) 50 | d_av = -0.053739 51 | e_av = 0.83433 52 | 53 | # Calculate k_h 54 | k_h = d_kh * log10(frequency) + e_kh 55 | for a, b, c in zip(a_kh, b_kh, c_kh): 56 | k_h += a * exp(-((log10(frequency) - b) / c) ** 2) 57 | 58 | k_h = 10**k_h 59 | 60 | # Calculate k_v 61 | k_v = d_kv * log10(frequency) + e_kv 62 | for a, b, c in zip(a_kv, b_kv, c_kv): 63 | k_v += a * exp(-((log10(frequency) - b) / c) ** 2) 64 | 65 | k_v = 10**k_v 66 | 67 | # Calculate alpha_h 68 | alpha_h = d_ah * log10(frequency) + e_ah 69 | for a, b, c in zip(a_ah, b_ah, c_ah): 70 | alpha_h += a * exp(-((log10(frequency) - b) / c) ** 2) 71 | 72 | # Calculate alpha_v 73 | alpha_v = d_av * log10(frequency) + e_av 74 | for a, b, c in zip(a_av, b_av, c_av): 75 | alpha_v += a * exp(-((log10(frequency) - b) / c) ** 2) 76 | 77 | # Calculate k and alpha based on elevation angle and polarization 78 | k = 0.5 * (k_h + k_v + (k_h - k_v) * cos(elevation_angle)**2 * cos(2. * polarization_tilt_angle)) 79 | alpha = 0.5 * (k_h * alpha_h + k_v * alpha_v + (k_h * alpha_h - k_v * alpha_v) * cos(elevation_angle)**2 * 80 | cos(2. * polarization_tilt_angle)) / k 81 | 82 | return k * rain_rate**alpha 83 | -------------------------------------------------------------------------------- /python/Chapter7/rgiqe_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "a06710f3", 6 | "metadata": {}, 7 | "source": [ 8 | "# Introduction to Synthetic Aperture Radar Using Python and MATLAB\n", 9 | "\n", 10 | "## by Andy Harrison - © Artech House 2022\n", 11 | "\n", 12 | "---\n", 13 | "\n", 14 | "## Example 7.6.6 Radar General Image Quality Equation\n", 15 | "---" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "id": "573bda20", 21 | "metadata": {}, 22 | "source": [ 23 | "**Import modules**" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 1, 29 | "id": "a452247a", 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "import numpy as np" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "id": "30a7936b", 39 | "metadata": {}, 40 | "source": [ 41 | "**Define the information content**" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 2, 47 | "id": "6803d6c0", 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "def rgiqe(delta_az, delta_r, grazing_angle, nesz):\n", 52 | " \"\"\"\n", 53 | " Calculate the information content.\n", 54 | " :param delta_az: The resolution in the azimuth direction (m).\n", 55 | " :param delta_r: The resolution in the range direction (m).\n", 56 | " :param grazing_angle: The grazing angle (deg).\n", 57 | " :param nesz: The NESZ (dB).\n", 58 | " :return: The information content (bits/m^2).\n", 59 | " \"\"\"\n", 60 | " \n", 61 | " # Calculate the support bands\n", 62 | " Ba = 1 / delta_az \n", 63 | "\n", 64 | " Brg = 1 / delta_r\n", 65 | " \n", 66 | " \n", 67 | " # Convert NESZ to linear units\n", 68 | " nesz = 10**(nesz / 10)\n", 69 | " \n", 70 | " \n", 71 | " # Calculate the information content (bits/m^2)\n", 72 | " return Ba * Brg * np.cos(np.radians(grazing_angle)) * np.log2(1 + 1 / nesz) " 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "id": "7de05a58", 78 | "metadata": {}, 79 | "source": [ 80 | "**Stripmap Mode**" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 3, 86 | "id": "8178c1d0", 87 | "metadata": {}, 88 | "outputs": [ 89 | { 90 | "name": "stdout", 91 | "output_type": "stream", 92 | "text": [ 93 | "Stripmap RGIQE 2.22\n" 94 | ] 95 | } 96 | ], 97 | "source": [ 98 | "# Set the azimuth resolution (m)\n", 99 | "delta_az = 0.5\n", 100 | "\n", 101 | "\n", 102 | "# Set the range resolution (m)\n", 103 | "delta_r = 3\n", 104 | "\n", 105 | "\n", 106 | "# Set the grazing angle (deg)\n", 107 | "grazing_angle = 60\n", 108 | "\n", 109 | "\n", 110 | "# Set the NESZ (dB)\n", 111 | "nesz = -20\n", 112 | "\n", 113 | "\n", 114 | "# Display the information content\n", 115 | "print(f'Stripmap RGIQE {rgiqe(delta_az, delta_r, grazing_angle, nesz):0.2f}')" 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "id": "97c5b449", 121 | "metadata": {}, 122 | "source": [ 123 | "**Spotlight Mode**" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 4, 129 | "id": "df936b3f", 130 | "metadata": {}, 131 | "outputs": [ 132 | { 133 | "name": "stdout", 134 | "output_type": "stream", 135 | "text": [ 136 | "Spotlight RGIQE 23.07\n" 137 | ] 138 | } 139 | ], 140 | "source": [ 141 | "# Set the azimuth resolution (m)\n", 142 | "delta_az = 0.5\n", 143 | "\n", 144 | "\n", 145 | "# Set the range resolution (m)\n", 146 | "delta_r = 0.25\n", 147 | "\n", 148 | "\n", 149 | "# Set the grazing angle (deg)\n", 150 | "grazing_angle = 55\n", 151 | "\n", 152 | "\n", 153 | "# Set the NESZ (dB)\n", 154 | "nesz = -15\n", 155 | "\n", 156 | "\n", 157 | "# Display the information content\n", 158 | "print(f'Spotlight RGIQE {rgiqe(delta_az, delta_r, grazing_angle, nesz):0.2f}')" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "id": "a673df44", 164 | "metadata": {}, 165 | "source": [ 166 | "**Scan Mode**" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 5, 172 | "id": "2bcd1a7b", 173 | "metadata": {}, 174 | "outputs": [ 175 | { 176 | "name": "stdout", 177 | "output_type": "stream", 178 | "text": [ 179 | "Scan RGIQE 0.10\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "# Set the azimuth resolution (m)\n", 185 | "delta_az = 5\n", 186 | "\n", 187 | "\n", 188 | "# Set the range resolution (m)\n", 189 | "delta_r = 5\n", 190 | "\n", 191 | "\n", 192 | "# Set the grazing angle (deg)\n", 193 | "grazing_angle = 70\n", 194 | "\n", 195 | "\n", 196 | "# Set the NESZ (dB)\n", 197 | "nesz = -22\n", 198 | "\n", 199 | "\n", 200 | "# Display the information content\n", 201 | "print(f'Scan RGIQE {rgiqe(delta_az, delta_r, grazing_angle, nesz):0.2f}')" 202 | ] 203 | } 204 | ], 205 | "metadata": { 206 | "kernelspec": { 207 | "display_name": "Python 3 (ipykernel)", 208 | "language": "python", 209 | "name": "python3" 210 | }, 211 | "language_info": { 212 | "codemirror_mode": { 213 | "name": "ipython", 214 | "version": 3 215 | }, 216 | "file_extension": ".py", 217 | "mimetype": "text/x-python", 218 | "name": "python", 219 | "nbconvert_exporter": "python", 220 | "pygments_lexer": "ipython3", 221 | "version": "3.10.1" 222 | } 223 | }, 224 | "nbformat": 4, 225 | "nbformat_minor": 5 226 | } 227 | -------------------------------------------------------------------------------- /python/Chapter7/spatial_resolution_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction to Synthetic Aperture Radar Using Python and MATLAB\n", 8 | "\n", 9 | "## by Andy Harrison - © Artech House 2022\n", 10 | "\n", 11 | "---\n", 12 | "\n", 13 | "## Example 7.6.1 Spatial Resolution\n", 14 | "---" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "**Import modules**" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "import numpy as np\n", 31 | "\n", 32 | "from scipy.constants import speed_of_light" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "**Set the operating frequency (Hz)**" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "operating_frequency = 10e9" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "**Calculate the wavelength (m)**" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 3, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "wavelength = speed_of_light / operating_frequency " 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "**Set the operating bandwidth (Hz)**" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 4, 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "bandwidth = 300e6" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "**Calculate the slant-plane range resolution (m)**" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 5, 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "name": "stdout", 97 | "output_type": "stream", 98 | "text": [ 99 | "Range resolution = 0.50 m\n" 100 | ] 101 | } 102 | ], 103 | "source": [ 104 | "range_resolution = speed_of_light / (2 * bandwidth)\n", 105 | "\n", 106 | "print(f'Range resolution = {range_resolution:.2f} m')" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "**Set the altitude (m)**" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 6, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "altitude = 570e3" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "**Set the incident angle (deg)**" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 7, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "incident_angle = 55" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "**Set the azimuth resolution (m)**" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": 8, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "azimuth_resolution = 0.25" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "**Calculate the slant range**" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 9, 167 | "metadata": {}, 168 | "outputs": [ 169 | { 170 | "name": "stdout", 171 | "output_type": "stream", 172 | "text": [ 173 | "Slant range = 695.84 km\n" 174 | ] 175 | } 176 | ], 177 | "source": [ 178 | "slant_range = altitude / np.cos(np.radians(90 - incident_angle))\n", 179 | "\n", 180 | "print(f'Slant range = {slant_range/1e3:.2f} km')" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "**Calculate the angle subtended by the synthetic aperture (rad)**" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 10, 193 | "metadata": {}, 194 | "outputs": [ 195 | { 196 | "name": "stdout", 197 | "output_type": "stream", 198 | "text": [ 199 | "Angle subtended by the synthetic aperture = 0.06 rad\n" 200 | ] 201 | } 202 | ], 203 | "source": [ 204 | "aperture_angle = wavelength / (2 * azimuth_resolution)\n", 205 | "\n", 206 | "print(f'Angle subtended by the synthetic aperture = {aperture_angle:.2f} rad')" 207 | ] 208 | }, 209 | { 210 | "cell_type": "markdown", 211 | "metadata": {}, 212 | "source": [ 213 | "**Calculate the synthetic aperture length (m)**" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": 11, 219 | "metadata": {}, 220 | "outputs": [ 221 | { 222 | "name": "stdout", 223 | "output_type": "stream", 224 | "text": [ 225 | "Synthetic aperture length = 41.73 km\n" 226 | ] 227 | } 228 | ], 229 | "source": [ 230 | "aperture_length = 2 * slant_range * np.tan(aperture_angle / 2)\n", 231 | "\n", 232 | "print(f'Synthetic aperture length = {aperture_length/1e3:.2f} km')" 233 | ] 234 | } 235 | ], 236 | "metadata": { 237 | "kernelspec": { 238 | "display_name": "Python 3 (ipykernel)", 239 | "language": "python", 240 | "name": "python3" 241 | }, 242 | "language_info": { 243 | "codemirror_mode": { 244 | "name": "ipython", 245 | "version": 3 246 | }, 247 | "file_extension": ".py", 248 | "mimetype": "text/x-python", 249 | "name": "python", 250 | "nbconvert_exporter": "python", 251 | "pygments_lexer": "ipython3", 252 | "version": "3.10.1" 253 | } 254 | }, 255 | "nbformat": 4, 256 | "nbformat_minor": 4 257 | } 258 | --------------------------------------------------------------------------------