I am modelling radar scenarios in python and am trying to make a model which retains phase information. To do so, I'm envisioning something which works like this:
- Generate samples of an LFM waveform at RF in a numpy array
- Calculate the distance to the target (two way)
- Divide the time take for the echo to return by the RF sampling rate to figure out the number of samples which the delay corresponds to
- Model the returned waveform as some SNR coefficient (unimportant for context of this question) multiplied by a copy of the transmitted waveform, and shift the elements in the array such that the echo is preceeded by a number of zero elements equal to the number of samples the delay corresponds to
- Do something with the phase...
It is this step 5 I am a little stuck with. In the steps leading up to this, I have accounted for the delay in a very 'discrete' way. That is, in time increments of the entire sample length. However, the true delay does not correspond to an integer number of samples. Therefore, I need to perform the sample shifting AND apply some phase shift to account for the fact that, due to the non-integer number of samples the shift corresponds to, the additional amount of shift can be represented by an offset in where in the waveform the sample is taken.
For example: say the delay corresponds to 37.465 samples. I can't shift the elements by this much, but I can shift them by 38 samples and say that, in each element, the point of the waveform at which the sample is measured is offset by a phase angle equivalent to this additional proportion of the wavelength.
As an example to illustrate what I mean: Assume the carrier frequency is 1 GHz. In this case, the period of the wavelength is 1/1e9. If we say the same rate is 2 GHz, each sample takes 1/2e9 seconds. Previously, I gave the example of a delay corresponding to 37.465 samples. 0.465 of a sample corresponds to a time of 0.465*1/2e9 seconds. In this length of time, the waveform cycles through (1/1e9) / (0.465 * 1/2e9) of a wavelength. I can calculate this as a phase angle and apply a phase shift to the array representing the returned signal to account for this.
Assuming this makes sense, my problem is then two-fold: Firstly, how do I apply such a phase shift? Secondly, how would this work for the LFM pulse where the frequency is changing over time (since, this means that the wavelength is changing over time and therefore the proportion of the wavelength which corresponds to that 0.465 of a sample changes from sample to sample within the waveform)?
Any help with how to approach this, in particular any actual code implementations would be highly appreciated. I will include the code I have so far in case that helps to understand how I'm modelling things.
Side note: I know real systems don't digitize at RF but I'm trying to create a model which allows the retention of full phase information, so I can't model at baseband.
CODE:
from numpy import *
# This function determines the number of samples which will be needed to model the waveform to allow a target up to the maximum range of interest
def find_chirp_sampling_requirements(max_range, carrier, bw):
sample_rate = 2*(carrier+bw)
time_per_sample = 1/rf_sample_rate
num_samples_required = int(ceil(((2*max_range)/299792458)/rf_time_per_sample))
return sample_rate, time_per_sample, num_samples_required
# This function returns the waveform as a numpy array
def generate_lfm(samples, time_per_sample, duty_cycle, bandwidth, carrier_frequency):
pulse_width_in_samples = int(samples*duty_cycle)
pulse_width_time = pulse_width_in_samples*time_per_sample
t_s = linspace(0,pulse_width_time,pulse_width_in_samples)
chirp_rate = bandwidth/pulse_width_time
f_low = carrier_frequency
initial_phase_offset = 0
phase_angle = 2*pi*(((chirp_rate*t_s**2)/2)+(f_low*t_s))+initial_phase_offset
complex_chirp = exp(1j*phase_angle)
waveform = zeros((samples), dtype=complex)
waveform[:shape(complex_chirp)[0]] += complex_chirp
return waveform
# This function returns the amount of phase angle which corresponds to the two way distance to the target
def calculate_two_way_phase_angle(distance, carrier_frequency):
wavelength = 299792458/carrier
wavelengths_in_distance = distance/wavelength
return wavelengths_in_distance*2*pi
# This function returns the time taken for the signal to return to the radar
def calculate_two_way_delay(distance):
return distance/299792458
# This function creates a time and phase shifted copy of the transmitted signal to model the received signal
def get_phase_and_sample_shifted_waveform(original_waveform, sample_shift, phase_shift):
# Perform phase shifting step
phase_shifted_waveform = DO SOMETHING TO 'original_waveform' HERE
# Perform integer number of samples shifting step
new_waveform = zeros_like(phase_shifted_waveform, dtype=complex)
new_waveform[sample_shift:] = phase_shifted_waveform[:-sample_shift]
return new_waveform
max_range = 10000
bandwidth = 0.05*10**9
carrier_frequency = 1*10**9
duty_cycle = 0.1
# Generate chirp waveform
sample_rate, time_per_sample, num_samples_required = find_chirp_sampling_requirements(max_range, carrier_frequency, bandwidth)
my_waveform = generate_lfm(num_samples_required, time_per_sample, duty_cycle, bandwidth, carrier_frequency)
distance_to_target = 5000
two_way_phase_angle = calculate_two_way_phase_angle(distance_to_target, carrier_frequency)
two_way_delay = calculate_two_way_delay(distance_to_target)
two_way_delay_in_samples = two_way_delay/time_per_sample
sample_remainder = int(ceil(two_way_delay_in_samples))-two_way_delay_in_samples
remainder_time = sample_remainder*time_per_sample
time_per_wavelength = 1/carrier_frequency
fraction_of_wavelength = remainder_time/time_per_wavelength
phase_shift = 2*pi*fraction_of_wavelength
phase_and_sample_shifted_waveform = get_phase_and_sample_shifted_waveform(my_waveform, two_way_delay_in_samples, phase_shift)
```