VSG60 AWGN using Python Example

Forums VSG Series Discussions VSG60 AWGN using Python Example

Viewing 6 posts - 1 through 6 (of 6 total)
  • Author
    Posts
  • #244470
    SignalHoundUser
    Participant

    This 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.
    #244472
    SignalHoundUser
    Participant

    Forum 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 arrays

    def 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] = 0

    adata_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_data

    def 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_parts

    return 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 iq

    def generate_iq():
    # Open device

    ret = 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 # dBm

    vsg_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()

    #244502
    AndrewAndrew
    Moderator

    The 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://relatedcontentdemo.kinsta.cloud/sigdownloads/SDK/online_docs/vsg60_api/index.html#autotoc_md9

    Opening 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.

    #244506
    SignalHoundUser
    Participant

    Thanks 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.

    #244767
    epanyuw
    Participant

    Hi Signalhounduser,

    Do you have chance to test the sync level between two VSG60As you are running on the same pc?

    #244768
    SignalHoundUser
    Participant

    What 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.

Viewing 6 posts - 1 through 6 (of 6 total)
  • You must be logged in to reply to this topic.