sdr.clock_error(x: ArrayLike, error: ArrayLike, error_rate: float = 0.0, center_freq: float | None = None, sample_rate: float | None = None) NDArray

Applies a clock error to the time-domain signal \(x[n]\).

This clock error could be caused by transmitter clock error, receiver clock error, or Doppler effects.

Parameters:
x: ArrayLike

The time-domain signal \(x[n]\) to which the clock error is applied.

Warning

The signal must be a real passband signal or a complex baseband signal (with 0 Hz baseband frequency).

If the signal is a real passband signal, time will be compressed resulting in a carrier frequency change. If the signal is a complex baseband signal, time will similarly be compressed. However, the zero-IF baseband signal will not observe a frequency shift, since it was always mixed to baseband. Therefore, there is a subsequent frequency shift corresponding to the expected frequency shift at passband.

If a complex low-IF signal is provided, the IF frequency will be shifted during time compression. This can become noticeable at high clock errors, e.g. 1,000 ppm or more. It is not advised to use this function with complex low-IF signals.

error: ArrayLike

The fractional clock error \(\epsilon\), which is unitless, with 0 representing no clock error. For example, 1e-6 represents 1 ppm of clock error.

The fractional clock error can be calculated from frequency offset \(\Delta f\) and carrier frequency \(f_c\) as \(\epsilon = \Delta f / f_c\). For example, a 1 kHz frequency error applied to a signal with a 1 GHz carrier frequency is 1e-6 or 1 ppm.

The fractional clock error can also be calculated from sample rate offset \(\Delta f_s\) and sample rate \(f_s\) as \(\epsilon = \Delta f_s / f_s\). For example, a 10 Hz sample rate error applied to a signal with a 1 MHz sample rate is 1e-5 or 10 ppm.

The fractional clock error can also be calculated from relative velocity \(\Delta v\) and speed of light \(c\) as \(\epsilon = \Delta v / c\). For example, a 60 mph (or 26.82 m/s) relative velocity between the transmitter and receiver is 8.946e-8 or 8.9 ppb.

error_rate: float = 0.0

The clock error \(\Delta \epsilon / \Delta t\) in 1/s.

center_freq: float | None = None

The center frequency \(f_c\) of the complex baseband signal in Hz. 0 Hz baseband frequency must correspond to the signal’s carrier frequency. If \(x[n]\) is complex, this must be provided.

sample_rate: float | None = None

The sample rate \(f_s\) in samples/s. If \(x[n]\) is complex, this must be provided.

Returns:

The signal \(x[n]\) with clock error applied.

Examples

This example demonstrates the effect of clock error on a real passband signal. The signal has a carrier frequency of 100 kHz. A frequency offset of 20 kHz is desired, corresponding to a clock error or 0.2. The clock error is added to the transmitter, and then removed at the receiver. Notice that the transmitted signal is compressed in time and shifted in frequency. Also notice that the corrected received signal matches the original.

In [1]: sample_rate = 2e6; \
   ...: freq = 100e3; \
   ...: duration = 1000e-6; \
   ...: x = sdr.sinusoid(duration, freq, sample_rate=sample_rate, complex=False)
   ...: 

In [2]: freq_offset = 20e3; \
   ...: error = freq_offset / freq; \
   ...: print("Clock error:", error); \
   ...: y = sdr.clock_error(x, error)
   ...: 
Clock error: 0.2

In [3]: error = -error / (1 + error); \
   ...: print("Clock error:", error); \
   ...: z = sdr.clock_error(y, error)
   ...: 
Clock error: -0.16666666666666669

In [4]: plt.figure(); \
   ...: sdr.plot.time_domain(x - 0, sample_rate=sample_rate, label="No clock error"); \
   ...: sdr.plot.time_domain(y - 3, sample_rate=sample_rate, label="Added Tx clock error"); \
   ...: sdr.plot.time_domain(z - 6, sample_rate=sample_rate, label="Removed Tx clock error"); \
   ...: plt.legend(loc="lower left"); \
   ...: plt.title("Real passband signals with and without clock error");
   ...: 

In [5]: plt.figure(); \
   ...: sdr.plot.dtft(x, sample_rate=sample_rate, label="No clock error"); \
   ...: sdr.plot.dtft(y, sample_rate=sample_rate, label="Added Tx clock error"); \
   ...: sdr.plot.dtft(z, sample_rate=sample_rate, label="Removed Tx clock error"); \
   ...: plt.axvline(freq, color="k", linestyle="--"); \
   ...: plt.axvline(freq + freq_offset, color="k", linestyle="--"); \
   ...: plt.xlim(80e3, 140e3);
   ...: 
../../_images/sdr_clock_error_1.png ../../_images/sdr_clock_error_2.png

This example demonstrates the effect of clock error on a complex baseband signal. The signal has a carrier frequency of 1 MHz and sample rate of 2 MS/s. A frequency offset of 100 kHz is desired, corresponding to a clock error of 0.1. The clock error is added to the transmitter, and then removed at the receiver. Notice that the transmitted signal is compressed in time, but not shifted in frequency. Notice that the transmitted signal is compressed in time and shifted in frequency. Also notice that the corrected received signal matches the original.

In [6]: sample_rate = 2e6; \
   ...: center_freq = 1e6; \
   ...: duration = 1000e-6; \
   ...: x = sdr.sinusoid(duration, 0, sample_rate=sample_rate)
   ...: 

In [7]: freq_offset = 100e3; \
   ...: error = freq_offset / center_freq; \
   ...: print("Clock error:", error); \
   ...: y = sdr.clock_error(x, error, 0, center_freq, sample_rate=sample_rate)
   ...: 
Clock error: 0.1

In [8]: error = -error / (1 + error); \
   ...: print("Clock error:", error); \
   ...: z = sdr.clock_error(y, error, 0, center_freq, sample_rate=sample_rate)
   ...: 
Clock error: -0.09090909090909091

In [9]: plt.figure(); \
   ...: sdr.plot.time_domain(x - 0 - 0j, sample_rate=sample_rate, label="No clock error"); \
   ...: sdr.plot.time_domain(y - 3 - 3j, sample_rate=sample_rate, label="Added Tx clock error"); \
   ...: sdr.plot.time_domain(z - 6 - 6j, sample_rate=sample_rate, label="Removed Tx clock error"); \
   ...: plt.legend(loc="lower left"); \
   ...: plt.title("Complex baseband signals with and without clock error");
   ...: 

In [10]: plt.figure(); \
   ....: sdr.plot.dtft(x, sample_rate=sample_rate, label="No clock error"); \
   ....: sdr.plot.dtft(y, sample_rate=sample_rate, label="Added Tx clock error"); \
   ....: sdr.plot.dtft(z, sample_rate=sample_rate, label="Removed Tx clock error"); \
   ....: plt.axvline(0, color="k", linestyle="--"); \
   ....: plt.axvline(freq_offset, color="k", linestyle="--"); \
   ....: plt.xlim(-20e3, 120e3);
   ....: 
../../_images/sdr_clock_error_3.png ../../_images/sdr_clock_error_4.png