Forums › VSG Series Discussions › VSG60 AWGN using Python Example
- This topic has 5 replies, 3 voices, and was last updated 1 year, 4 months ago by
SignalHoundUser.
- AuthorPosts
SignalHoundUserParticipantThis is example code I created to generate a AWGN signal using the Python library. I have tested this against a SM200A and the power levels and BW are spot on. The attached Spike plot shows the comparison with the Signal Hound GUI version. Differences to note are the stronger out of band signals on my version (blue trace). This is fine for my purposes, but if anyone has any ideas I can try to improve I will give it a go.
I have attached the python code. Its fairly self explanatory.
Why am I doing this?! I need to automate the testing of some radios. I need 2 features not provided in the GUI.
1. Syncronised control of multiple devices.
2. Ability to automatically ramp the power level of multiple devices. Either linked or individually.I will post in another thread, but I am really struggling with getting multiple devices to work with the Python bindings. Any help would be appreciated.
Attachments:
You must be logged in to view attached files.
SignalHoundUserParticipantForum wont allow .py files. Hopefully the txt will work.
# -*- coding: utf-8 -*-
# This example generates a basic AWGN signal.
from vsgdevice.vsg_api import *
from time import sleep
import matplotlib.pyplot as plt
#numpy.set_printoptions(threshold=numpy.inf) #Use to print full length arraysdef plot_complex_fft(complex_data, sampling_rate):
# Calculate and plot the Frequency data
# Calculate FFT
fft_result = numpy.fft.fft(complex_data)
fft_freq = numpy.fft.fftfreq(len(complex_data), 1 / sampling_rate)
# print(fft_freq)
# Plot the results
plt.figure(figsize=(10, 6))# Plot the frequency-domain signal (FFT)
plt.plot(fft_freq, numpy.real(fft_result), color=’red’, label=’I Data’)
plt.plot(fft_freq, numpy.imag(fft_result), color=’blue’, label=’Q Data’)
# plt.plot(numpy.abs(fft_result))
plt.title(‘FFT of Complex Signal’)
plt.xlabel(‘Frequency (Hz)’)
plt.ylabel(‘Amplitude’)plt.show()
def plot_IQ(complex_data, sampling_rate):
# Plot the Time Series results
plt.figure(figsize=(10, 6))
# Separate the real and imaginary parts
I_data = numpy.real(complex_data)
Q_data = numpy.imag(complex_data)
# Plot the frequency-domain signal (FFT)
plt.plot(I_data, color=’red’, label=’I Data’)
plt.plot(Q_data, color=’blue’, label=’Q Data’)# plt.plot(numpy.abs(fft_result))
plt.title(‘Complex Signal’)
plt.xlabel(‘Samples’)
plt.ylabel(‘Amplitude’)plt.show()
def low_pass_filter(complex_data: numpy.ndarray, BW: int = 40e6, sampling_rate: int = 50e6) -> numpy.ndarray:
# translate bandlimit from Hz to dataindex according to sampling rate and data size
bandlimit_index = int(BW * (complex_data.size/2) / sampling_rate)
fsig = numpy.fft.fft(complex_data)for i in range(bandlimit_index + 1, len(fsig) – bandlimit_index):
fsig[i] = 0adata_filtered = numpy.fft.ifft(fsig)
return adata_filtered
def normalise_level(complex_data):
# Normalise average level to 0dB
aveSig = numpy.average(numpy.abs(complex_data))
aveSigdB = 20*numpy.log10(aveSig) # should be 0dB
print(“Normalised Signal dB = ” + str(aveSigdB))
complex_data *= 1/aveSig
aveSig = numpy.average(numpy.abs(complex_data))
aveSigdB = 20*numpy.log10(aveSig) # should be 0dB
print(“Normalised Signal dB = ” + str(aveSigdB))return complex_data
def interleaved_to_complex(interleaved_data):
# Reshape the interleaved data into a complex array
complex_data = interleaved_data[0::2] + 1j * interleaved_data[1::2]
return complex_datadef complex_to_interleaved(complex_data):
# Separate the real and imaginary parts
real_parts = numpy.real(complex_data)
imag_parts = numpy.imag(complex_data)# Interleave the real and imaginary parts
interleaved_iq = numpy.empty(2 * len(complex_data), dtype=numpy.float32)
interleaved_iq[0::2] = real_parts
interleaved_iq[1::2] = imag_partsreturn interleaved_iq
def complex_AWGN(length, stddev):
iq = numpy.random.normal(0, stddev, length) + 1j * \
numpy.random.normal(0, stddev, length) # .astype(numpy.float32)
return iqdef generate_iq():
# Open deviceret = vsg_open_device()
print(ret)
handle = ret[“handle”]serialNumber = vsg_get_serial_number(handle)[“serial”]
# Configure generator
freq = 1.0e9 # Hz
sample_rate = 50.0e6 # samples per second
BW = 40.0e6 # Target bandwidth of AWGN
level = -20.0 # dBmvsg_set_frequency(handle, freq)
vsg_set_level(handle, level)
vsg_set_sample_rate(handle, sample_rate)
vsg_recal(handle)# Gernerate Waveform
iq = complex_AWGN(16384, 100)
iq = low_pass_filter(iq, BW, sample_rate)
iq = normalise_level(iq) #Set IQ to 0dB
iq = complex_to_interleaved(iq)vsg_repeat_waveform(handle, iq.astype(numpy.float32), int(len(iq)/2))
print(“Waveform set”)# Ramp Power
for power in range(-100, -30, 5):vsg_set_level(handle, power)
print(“Power = ” + str(power))
scaling = vsg_get_IQ_scale(handle)[“iq_scale”]
print(“scaling = ” + str(scaling))
sleep(1)
print(“complete”)
# Stop waveform
vsg_abort(handle)# Done with device
vsg_close_device(handle)
print(“closed”)if __name__ == “__main__”:
generate_iq()
AndrewModeratorThe API certainly should be able to interface several devices. We interface up to 8 devices in our manufacturing line using the API. What you will find is that most PCs will limit the number of actively streaming VSG60’s to 2, primarily limited by USB throughput and CPU cycles. Some PCs do struggle to transmit on 2 simultaneously. If you are also using one of our spectrum analyzers, you might consider moving the analyzer to another PC for making measurements.
If you are on Linux, please be sure to read the section on Linux in the VSG60 manual,
https://signalhound.com/sigdownloads/SDK/online_docs/vsg60_api/index.html#autotoc_md9Opening a second device should be as simple as calling vsgOpenDevice a second time with a new handle variable. This will result in you having two handles, one for each device. The handle values should be 0 and 1 after the vsgOpenDevice function returns.
If you are on Windows, you can verify that you see 2 devices connected in the device manager. If you don’t, then you need to resolve this first. The LED should also be solid green on both units when connected and idle.
We have not tested multiple devices in our Python environment, but it should be the same. If possible, you can test multiple devices in C++. The Python wrapper simply wraps the C interface. You can see the function assignment in the vsg_api.py file.
SignalHoundUserParticipantThanks Andrew,
Got it working now. I think it was down to the power requirements for running 2 VSGs from one hub on my dev laptop. I successfully got it working yesterday with 1 VSG and 1 SM200 running from the same usb3 hub. And the other vsg plugged into a seperate port. The SM200 is not bus powered.
All works fine now. I hope to try 5 devices today.
epanyuwParticipantHi Signalhounduser,
Do you have chance to test the sync level between two VSG60As you are running on the same pc?
SignalHoundUserParticipantWhat do you mean by sync level?
I haven’t tried or done any experiments on time synchronisation. With the current setup there is a definate lag between changing the power levels of each signal. This could be related to the length of the arm waveform.
My next steps will be to add Crest Factor Reduction to improve the PAPR and hopefully improve the signal quality at higher power levels.
- AuthorPosts
You must be logged in to reply to this topic.