Forums › VSG Series Discussions › VSG60 AWGN using Python Example

- This topic has 5 replies, 3 voices, and was last updated 6 months, 1 week 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.