Commit c896b1b6 authored by Smail Bachir's avatar Smail Bachir Committed by Thomas Gambier

Small improvements

parent de5b8855
......@@ -4,7 +4,7 @@ Pre-Distortion for Linearization of RF power amplifier in python using ADALM PLU
This program is released under GPL version 3 or any later version.
## Overview
The pyPlutoDPD is a python implementation of Digital Pre-Distortion, a most used technique to linearize the distortions introiduced by the RF Power Amplifiers.
The pyPlutoDPD is a python implementation of Digital Pre-Distortion, a most used technique to linearize the distortions introduced by the RF Power Amplifiers.
This has been developed under python by: </br>
- Smail Bachir, University of Poitiers, XLIM-Laboratory UMR-CNRS 7252
......@@ -17,13 +17,29 @@ in collaboration with:
LMS Algorithm running with a polular ADALM-PlutoSDR has been implemneted.
<b> Pre-requises </b> : pyPlutoDPD requires only free dependances :
- Python with packages : numpy, adi, matplotlib, scipy
- Python with packages : numpy, adi, matplotlib, scipy, time
- Install PlutoSDR in Python : see https://pysdr.org/content/pluto.html
- Cheap board : Adalm PlutoSDR
<b> Setup </b> : pyPlutoDPD can be used with two configuations
1. Minimale configuration with only ADALM-Pluto SDR connected to himself (SMA wire bettween TX and RX). This allows to test the program and see the steps.
2. Configuration with DUT (Device Under Test) : Here, insert your RF-PA bettween TX and RX. In our case, we linearize a RF PA of .....
1. For initial test with only ADALM-Pluto connected to himself (SMA wire bettween TX and RX). This allows to test the program and see the steps.
<img src="./img/pluto_alone.png" width="500" />
2. Configuration with DUT (Device Under Test) : Here, insert your RF-PA bettween TX and RX. In our case, we linearize an asymmetrical 10W/2-Stage Doherty Amplifier Module (GaN on SiC Technology)
<img src="./img/pluto_dut.png" width="500" />
## Setting parameters
- fileRx : The file containing baseband samples (in .bin, .mat, .csv, .txt, ...)
- sample_rate : sampling frequency (in MHz). Here 61.44 MHz
- osf : the used Over-Sampling Factor for I/Q samples. Here osf = 3
- freq : RF frequency (in Hz). Here 3650 MHz
- initialGain : ADALM-Pluto TX gain (in dB. Here -73dB for board safety
- sdr.rx_hardwaregain_chan0 : ADALM-Pluto RX gain (in dB)
- [Ka, La, Kb, Lb, Mb, Kc, Lc, Mc] : GMP model orders. Here [6, 2, 2, 2, 1, 0, 1, 1]
# Main program
......@@ -39,8 +55,6 @@ from scipy.signal import get_window
from scipy.fft import fft, fftshift
from scipy import signal
import time
start = time.time() # start time to measure the execution time
```
Load the used functions located in the ./commonFunctions.py
......@@ -55,12 +69,12 @@ from commonFunctions import * # XLIM used functions
### Transmit original samples
Load transmitted samples en .bin format
The preambule samples are used for synchronization
You can use "lte-5G/BW-10MHz with 61.44MHz of sampling rate" or "WLAN/BW-10MHz with 60MHz of sampling sate"
You can use "lte-5G/BW-20MHz with 61.44MHz of sampling rate" or "WLAN/BW-20MHz with 60MHz of sampling sate"
```python
# Read the frame
path = ""
path = "./samples/"
osf = 3
filePreambule = "preambule_BW20osf" + str(osf) + "_single.bin"
fileRx = "lteBW20osf" + str(osf) + "_single.bin"
......@@ -104,22 +118,22 @@ Config Tx and start transmitting
freq = int(3650)
win_fft = 'flattop' # The used window for FFT ans spectrum plot
initialGain = int(0) # Here, the initial gain of TX is set to the minimal value for safety
initialGain = int(-73) # Here, the initial gain of TX is set to the minimal value for safety
sdr.tx_rf_bandwidth = int(sample_rate) # filter cutoff
sdr.tx_lo = freq*10**6
sdr.tx_hardwaregain_chan0 = initialGain #
sdr.tx_destroy_buffer() # Stop transmitting for safe
sdr.tx_cyclic_buffer = True # Enable cyclic buffers
safetyGain = float(0) # set your tx gain limitation according to your DUT
safetyGain = float(-10) # set your tx gain limitation according to your DUT
sdr.tx(samples) # start transmitting
```
Loop for tx_gain monitoring in real time
# Loop for tx_gain monitoring in real time
```python
valid = False
valid = True
while (valid):
valid = input("1 to set txGain || 0 to compute DPD : ")
valid = bool(int(valid))
......@@ -130,8 +144,15 @@ while (valid):
sdr.tx_hardwaregain_chan0 = gain
else:
sdr.tx_hardwaregain_chan0 = safetyGain
start = time.time() # start time to measure the execution time just befor tranmitting
```
1 to set txGain || 0 to compute DPD : 1
enter new txGain (Actual txGain = -73) : 0
1 to set txGain || 0 to compute DPD : 0
### Receive the original samples
......@@ -140,7 +161,7 @@ sdr.rx_lo = sdr.tx_lo
sdr.rx_rf_bandwidth = sdr.tx_rf_bandwidth
sdr.rx_buffer_size = 3*num_samps # 3 times to capture one sequence
sdr.gain_control_mode_chan0 = 'manual'
sdr.rx_hardwaregain_chan0 = 8 # set the receive gain, but be careful not to saturate the ADC
sdr.rx_hardwaregain_chan0 = 15 # set the receive gain, but be careful not to saturate the ADC
```
Clear buffer just to be safe
......@@ -158,10 +179,10 @@ Receive samples
rx_samples = sdr.rx()
avg_pwr = np.mean(np.abs(rx_samples/coeff)**2)
avg_pwr_dB = round(10*np.log10(avg_pwr),2)
print("check_rx = ", avg_pwr_dB, ": Best in -30 +/- 5. Else, adjust the rx_hardwaregain")
print("check_rx = ", avg_pwr_dB, ": Best in -30 +/- 3. Else, adjust the rx_hardwaregain")
```
check_rx = -29.38 : Best in -30 +/- 5. Else, adjust the rx_hardwaregain
check_rx = -32.49 : Best in -30 +/- 3. Else, adjust the rx_hardwaregain
Synchronization and coarse phase correction using the preambule (zeroing AM/PM)
......@@ -191,9 +212,7 @@ plt.legend(['Original signal'])
plt.show()
```
![png](img/output_26_0.png)
<img src="./img/spectre_without.png" width="500" />
......@@ -205,9 +224,7 @@ amam_ampm_plot(paIn, paOut)
plt.show()
```
![png](img/output_28_0.png)
<img src="./img/am_pm_without.png" width="500" />
......@@ -223,7 +240,7 @@ $y_{dpd}(n)$ : dpd output (complex envelope)
```python
dpdModel = {'fs': sample_rate}
[Ka, La, Kb, Lb, Mb, Kc, Lc, Mc] = [7, 1, 0, 1, 1, 0, 1, 1] # set the dpd orders and memory depths
[Ka, La, Kb, Lb, Mb, Kc, Lc, Mc] = [6, 2, 2, 2, 1, 0, 1, 1] # set the dpd orders and memory depths
dpdModel['parameters'] = {'Ka':Ka, 'La':La, 'Kb':Kb, 'Lb':Lb, 'Mb':Mb, 'Kc':Kc, 'Lc':Lc, 'Mc':Mc};
gainATT = 1
......@@ -239,8 +256,8 @@ print("dpd tx-power = ", txPower, " dBm")
dpdOutput *= coeff # scaling for ADALM-Pluto
```
nmse of identification : -32.15 dB
dpd tx-power = -11.255 dBm
nmse of identification : -32.41 dB
dpd tx-power = -10.506 dBm
## Transmission with DPD
......@@ -253,8 +270,9 @@ sdr.tx_destroy_buffer() # Stop transmitting for safe
sdr.tx_cyclic_buffer = True # Enable cyclic buffers
sdr.tx(dpdOutput) # start transmitting with DPD
end = time.time() # end of DPD computation
valid = False
valid = True
while (valid):
valid = input("1 to set txGain || 0 to compute DPD : ")
valid = bool(int(valid))
......@@ -270,6 +288,9 @@ while (valid):
# np.savetxt('outfile.txt', rx_samples.view(complex))
```
1 to set txGain || 0 to compute DPD : 0
### Receive with DPD signal
......@@ -280,7 +301,7 @@ avg_pwr_dB = round(10*np.log10(avg_pwr),2)
print("check_rx = ", avg_pwr_dB, ": Best in -30 +/- 5. Else, adjust the rx_hardwaregain")
```
check_rx = -29.39 : Best in -30 +/- 5. Else, adjust the rx_hardwaregain
check_rx = -32.48 : Best in -30 +/- 5. Else, adjust the rx_hardwaregain
Synchronization of input and output
......@@ -300,18 +321,14 @@ Plot Linearized AM/AM and AM/PM
```python
amam_ampm_plot(paIn, paLin)
plt.show()
print("nmse after linearization : ", np.round(nmse(paIn, paLin), 2), " dB")
#print("nmse after linearization : ", np.round(nmse(paIn, paLin), 2), " dB")
```
![png](img/output_40_0.png)
<img src="./img/am_pm_with.png" width="500" />
nmse after linearization : -20.5 dB
```python
plt.figure()
......@@ -337,7 +354,7 @@ plt.show()
![png](img/output_41_0.png)
![png](img/output_42_0.png)
......@@ -353,9 +370,7 @@ plt.xlabel('Frequency (MHz)'); plt.ylabel('Spectrum'); plt.legend(['With DPD'])
plt.show()
```
![png](img/output_43_0.png)
<img src="./img/spectre_with.png" width="500" />
......@@ -363,6 +378,7 @@ Stop transmitting
```python
sdr.tx_hardwaregain_chan0 = -73
sdr.tx_destroy_buffer()
```
......@@ -370,25 +386,10 @@ Print the DPD coefficients
```python
end = time.time()
elapsed = end - start
print(f'Elapsed time : {elapsed:.2}ms')
```
Elapsed time : 3.5ms
```python
for i in dpdModel['coefGMP']:
print(np.round(i,3))
```
Elapsed time : 1.1ms
(0.911+0.003j)
(-0.163-0.035j)
(0.934+0.218j)
(-2.078-0.554j)
(2.636+0.797j)
(-1.658-0.524j)
(0.414+0.105j)
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -8,25 +8,26 @@
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
#
# See COPYING file for full licensing terms.
#
##############################################################################
# # pyPlutoDPD
# Pre-Distortion for Linearization of RF power amplifier in python using ADALM PLUTO.
# ## Overview
# The pyPlutoDPD is a python implementation of Digital Pre-Distortion, a most used technique to linearize the distortions introiduced by the RF Power Amplifiers.
# The pyPlutoDPD is a python implementation of Digital Pre-Distortion, a most used technique to linearize the distortions introduced by the RF Power Amplifiers.
#
# This has been developed under python by:
# - Smail Bachir, University of Poitiers, XLIM-Laboratory UMR-CNRS 7252
......@@ -39,22 +40,37 @@
# LMS Algorithm running with a polular ADALM-PlutoSDR has been implemneted.
#
# <b> Pre-requises </b> : pyPlutoDPD requires only free dependances :
# - Python with packages : numpy, adi, matplotlib, scipy
# - Python with packages : numpy, adi, matplotlib, scipy, time
# - Install PlutoSDR in Python : see https://pysdr.org/content/pluto.html
# - Cheap board : Adalm PlutoSDR
#
# <b> Setup </b> : pyPlutoDPD can be used with two configuations
# 1. Minimale configuration with only ADALM-Pluto SDR connected to himself (SMA wire bettween TX and RX). This allows to test the program and see the steps.
# 2. Configuration with DUT (Device Under Test) : Here, insert your RF-PA bettween TX and RX. In our case, we linearize a RF PA of .....
# 1. For initial test with only ADALM-Pluto connected to himself (SMA wire bettween TX and RX). This allows to test the program and see the steps.
#
# <img src="./img/pluto_alone.png" width="500" />
#
#
# 2. Configuration with DUT (Device Under Test) : Here, insert your RF-PA bettween TX and RX. In our case, we linearize an asymmetrical 10W/2-Stage Doherty Amplifier Module (GaN on SiC Technology)
#
# <img src="./img/pluto_dut.png" width="500" />
#
# ## Setting parameters
# - fileRx : The file containing baseband samples (in .bin, .mat, .csv, .txt, ...)
# - sample_rate : sampling frequency (in MHz). Here 61.44 MHz
# - osf : the used Over-Sampling Factor for I/Q samples. Here osf = 3
# - freq : RF frequency (in Hz). Here 3650 MHz
# - initialGain : ADALM-Pluto TX gain (in dB. Here -73dB for board safety
# - sdr.rx_hardwaregain_chan0 : ADALM-Pluto RX gain (in dB)
# - [Ka, La, Kb, Lb, Mb, Kc, Lc, Mc] : GMP model orders. Here [6, 2, 2, 2, 1, 0, 1, 1]
# # Main program
# ### Required packages
# In[47]:
# In[27]:
from commonFunctions import * # XLIM used functions
import numpy as np
import adi # API for Adalm-Pluto
import matplotlib.pyplot as plt
......@@ -64,12 +80,13 @@ from scipy.fft import fft, fftshift
from scipy import signal
import time
start = time.time() # start time to measure the execution time
# Load the used functions located in the ./commonFunctions.py
# In[48]:
# In[28]:
from commonFunctions import * # XLIM used functions
# ## Transmission without DPD
......@@ -77,13 +94,13 @@ start = time.time() # start time to measure the execution time
# ### Transmit original samples
# Load transmitted samples en .bin format
# The preambule samples are used for synchronization
# You can use "lte-5G/BW-10MHz with 61.44MHz of sampling rate" or "WLAN/BW-10MHz with 60MHz of sampling sate"
# You can use "lte-5G/BW-20MHz with 61.44MHz of sampling rate" or "WLAN/BW-20MHz with 60MHz of sampling rate"
# In[49]:
# In[29]:
# Read the frame
path = ""
path = "./samples/"
osf = 3
filePreambule = "preambule_BW20osf" + str(osf) + "_single.bin"
fileRx = "lteBW20osf" + str(osf) + "_single.bin"
......@@ -111,7 +128,7 @@ samples *= coeff
# Connect the sdr pluto
# In[50]:
# In[30]:
sdr = adi.Pluto("ip:192.168.2.1")
......@@ -120,30 +137,30 @@ sdr.sample_rate = sample_rate
# Config Tx and start transmitting
# In[51]:
# In[31]:
freq = int(3650)
win_fft = 'flattop' # The used window for FFT ans spectrum plot
# Here, the initial gain of TX is set to the minimal value for safety
initialGain = int(0)
initialGain = int(-73)
sdr.tx_rf_bandwidth = int(sample_rate) # filter cutoff
sdr.tx_lo = freq*10**6
sdr.tx_hardwaregain_chan0 = initialGain
sdr.tx_destroy_buffer() # Stop transmitting for safe
sdr.tx_cyclic_buffer = True # Enable cyclic buffers
safetyGain = float(0) # set your tx gain limitation according to your DUT
safetyGain = float(-10) # set your tx gain limitation according to your DUT
sdr.tx(samples) # start transmitting
# Loop for tx_gain monitoring in real time
# # Loop for tx_gain monitoring in real time
# In[52]:
# In[32]:
valid = False
valid = True
while (valid):
valid = input("1 to set txGain || 0 to compute DPD : ")
valid = bool(int(valid))
......@@ -156,10 +173,12 @@ while (valid):
else:
sdr.tx_hardwaregain_chan0 = safetyGain
start = time.time() # start time to measure the execution time just befor tranmitting
# ### Receive the original samples
# In[53]:
# In[33]:
sdr.rx_lo = sdr.tx_lo
......@@ -167,12 +186,12 @@ sdr.rx_rf_bandwidth = sdr.tx_rf_bandwidth
sdr.rx_buffer_size = 3*num_samps # 3 times to capture one sequence
sdr.gain_control_mode_chan0 = 'manual'
# set the receive gain, but be careful not to saturate the ADC
sdr.rx_hardwaregain_chan0 = 8
sdr.rx_hardwaregain_chan0 = 15
# Clear buffer just to be safe
# In[54]:
# In[34]:
for i in range(0, 10):
......@@ -181,26 +200,26 @@ for i in range(0, 10):
# Receive samples
# In[55]:
# In[35]:
rx_samples = sdr.rx()
avg_pwr = np.mean(np.abs(rx_samples/coeff)**2)
avg_pwr_dB = round(10*np.log10(avg_pwr), 2)
print("check_rx = ", avg_pwr_dB,
": Best in -30 +/- 5. Else, adjust the rx_hardwaregain")
": Best in -30 +/- 3. Else, adjust the rx_hardwaregain")
# Synchronization and coarse phase correction using the preambule (zeroing AM/PM)
# In[56]:
# In[36]:
# Synchronization using cross-corelation
[paIn, paOut] = corelation(samples, rx_samples, osf)
# In[57]:
# In[37]:
# Coarse phase correction using the preambule (zeroing AM/PM)
......@@ -211,7 +230,7 @@ paOut /= np.max(np.abs(paOut)) # scaling
# Plot freq domain
# In[58]:
# In[38]:
[pxxWithout, f] = toPlotSpectrum(paOut, sample_rate, win_fft, 20, "all")
......@@ -226,7 +245,7 @@ plt.show()
# Plot AM/AM and AM/PM
# In[59]:
# In[39]:
amam_ampm_plot(paIn, paOut)
......@@ -242,12 +261,12 @@ plt.show()
# $x(n)$ : dpd input (complex envelope) <br />
# $y_{dpd}(n)$ : dpd output (complex envelope)
# In[37]:
# In[40]:
dpdModel = {'fs': sample_rate}
# set the dpd orders and memory depths
[Ka, La, Kb, Lb, Mb, Kc, Lc, Mc] = [7, 1, 0, 1, 1, 0, 1, 1]
[Ka, La, Kb, Lb, Mb, Kc, Lc, Mc] = [6, 2, 2, 2, 1, 0, 1, 1]
dpdModel['parameters'] = {'Ka': Ka, 'La': La, 'Kb': Kb,
'Lb': Lb, 'Mb': Mb, 'Kc': Kc, 'Lc': Lc, 'Mc': Mc}
......@@ -268,15 +287,16 @@ dpdOutput *= coeff # scaling for ADALM-Pluto
# ### Transmit DPD signal
# In[38]:
# In[41]:
sdr.tx_destroy_buffer() # Stop transmitting for safe
sdr.tx_cyclic_buffer = True # Enable cyclic buffers
sdr.tx(dpdOutput) # start transmitting with DPD
end = time.time() # end of DPD computation
valid = False
valid = True
while (valid):
valid = input("1 to set txGain || 0 to compute DPD : ")
valid = bool(int(valid))
......@@ -295,7 +315,7 @@ while (valid):
# ### Receive with DPD signal
# In[39]:
# In[42]:
rx_samples = sdr.rx()
......@@ -307,7 +327,7 @@ print("check_rx = ", avg_pwr_dB,
# Synchronization of input and output
# In[40]:
# In[43]:
[paIn, paLin] = corelation(samples, rx_samples, osf)
......@@ -319,15 +339,15 @@ paLin /= np.max(np.abs(paLin)) # scaling
# Plot Linearized AM/AM and AM/PM
# In[41]:
# In[44]:
amam_ampm_plot(paIn, paLin)
plt.show()
print("nmse after linearization : ", np.round(nmse(paIn, paLin), 2), " dB")
# print("nmse after linearization : ", np.round(nmse(paIn, paLin), 2), " dB")
# In[42]:
# In[45]:
plt.figure()
......@@ -354,7 +374,7 @@ plt.show()
# Compare spectrum with and without DPD
# In[43]:
# In[46]:
[pxxWith, f] = toPlotSpectrum(paLin, sample_rate, win_fft, 20, "all")
......@@ -369,24 +389,17 @@ plt.show()
# Stop transmitting
# In[44]:
# In[47]:
sdr.tx_hardwaregain_chan0 = -73
sdr.tx_destroy_buffer()
# Print the DPD coefficients
# In[45]:
# In[48]:
end = time.time()
elapsed = end - start
print(f'Elapsed time : {elapsed:.2}ms')
# In[46]:
for i in dpdModel['coefGMP']:
print(np.round(i, 3))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment