RFSoC 2x2 Phase Measurement

Hi, I am attempting to measure the gain phase and magnitude spectrum through various filters (hardware) using the RFSoC 2x2. Currently, I have the code for the gain magnitude working and reliable. However, I am unable determine phase data reliably.

What I would like to do is use the ADC mixer to convert the phase and gain data into DC signals. Then, I apply a digital low pass filter (spicy butter) to measure only the DC component of the signal. From here I should be able to recover the gain and phase data easily. I will attach a PDF of this math below to show the calculations I am describing.

Mar2022.pdf (191.8 KB)

Unfortunately, the ADC mixing does not appear to be consistent. For example, when I test the response at 1 GHz I get a phase of say -2. If I keep making to measurements at 1 GHz, my phase moves around -2 with a normal amount of noise. However, if I tune the ADC to 100 MHz and then tune it back to 1 GHz, my answer for the phase changes radically, for example to 0.6. This would explain why my phase spectrum is nonsense. My understanding is that there is likely some QMC correction which I need to do after switching frequencies. However, it is not clear what this correction would be and how I would determine it.

The code and output are attached below (measuring a high pass filter).

from pynq.overlays.base import BaseOverlay
import xrfdc
import plotly.graph_objs as go
import numpy as np
import ipywidgets as ipw
from scipy import signal
import time
from tqdm.notebook import tqdm

base = BaseOverlay('base.bit')


frequency_list = np.logspace(2,3,25) #Use log space
transmitter_amplitude = 0.5
number_samples = 2048
sample_frequency = 2048e6

def update_adc_mixers(channel, freq, phase):
    channel.adc_block.MixerSettings['Freq'] = freq
    channel.adc_block.MixerSettings['PhaseOffset'] = phase

def update_dac_mixers(channel, enable, gain, frequency, phase):
    channel.control.enable = enable
    channel.control.gain = gain
    channel.dac_block.MixerSettings['Freq'] = frequency
    channel.dac_block.MixerSettings['PhaseOffset'] = phase

def calculate_gain_magnitude_phase(ia, qa, ib, qb, phi_a, phi_b):
    return [np.mean(np.sqrt(ib**2+qb**2)/np.sqrt(ia**2+qa**2)), np.mean(phi_a-phi_b)]

def run_spectrum_sweep(frequencies = frequency_list,
                       amplitude = transmitter_amplitude):
    magnitude = []
    phase = []
    results = []

    for freq in tqdm(frequencies):
        update_dac_mixers(base.radio.transmitter.channel[1], True, transmitter_amplitude, freq, 0)
        update_adc_mixers(base.radio.receiver.channel[0], freq , 0)
        update_adc_mixers(base.radio.receiver.channel[1], freq, 0)

        cdata = []
        for i in range(0, len(base.radio.receiver.channel)):

        sos = signal.butter(8, freq/2.0*1e6, 'lp', fs=2048e6, output='sos')
        cdata[0] = signal.sosfilt(sos, cdata[0])
        cdata[1] = signal.sosfilt(sos, cdata[1])
        samples=int(number_samples/100*np.log(freq)**2) #This is just to average DC component over many cycles after the digital filter is applied. Somewhat arbitrary quantity, but definitely not the issue.
        phi_a=np.arctan2(qa, ia)
        phi_b=np.arctan2(qb, ib)
        computed_data=calculate_gain_magnitude_phase(ia,qa,ib,qb, phi_a, phi_b)
    return [magnitude, phase]


The gain magnitude and phase are shown below. Frequency (MHz) is on the x-axis. The first plot shows gain magnitude, the second shows phase (not really) in radians.

Are there any remedies for the phase data? Am I misunderstanding the ADC mixing process and the errors it introduces? Any help to get more accurate phase data would be appreciated. Thank you.

1 Like

Hi there,

I’m probably misunderstanding something, but why aren’t you using a tone generator? Do you still need to set the DAC if there is no tone?

One thing that could be happening is updating the mixer settings causes it to have some form of small interruption in transmission/reception. This could cause a timing delay between ADC/DAC and result in a phase difference. You could confirm this by having your ADC tuned at the consistent frequency you confirmed to be correct (the keeping it at 1GHz scenario), and just update the mixer settings to the exact same frequency. If that is what is happening then you should observe one of those drastic phase changes.


Hi Shawn,

Thank you for the reply. Theres no particular reason I am not using a tone generator, though I have just confirmed that exchanging the update_DAC for a tone generator does not make a difference in the phase issue. I am still generating a tone, just more manually.

I think your analysis is correct that it is a timing issue in the ADC settings. However, I tested the constant 1GHz frequency as you suggested, updated the ADC mixer to the same frequency and there was no phase shift induced! The only source of this phase is when we change between two different frequencies. So maybe in changing the frequency there is a time delay?

On the other hand, it seems like we should be able to recover the phase despite a time lag so long as the ADC mixer components are orthogonal? (Referring to the math attached)

Thank you!

1 Like

Hi Tommy,

Apologies for late reply. Have you tried setting something else different, other than the frequency? That’s interesting about the tones… My understanding was that if you are sampling noise the phase you get should be random as well.

Can’t comment on math too much, but when dealing with real world circuits there can be a lot of unexpected behaviors… Just to eliminate that possibility, have you tried simulating this with your equations? Generating signals in numpy, or similar, and seeing if your calculations are consistent with the theory? If you have a working simulation then you can worry about what the hardware is doing to cause different results.


1 Like

No worries, thank you for the help! I did a few simulations and found that the math works as intended. I ran a few more test to try diagnose this some more. The first test was to set the ADC mixer frequency to 0. Then there should only be a Real component to the response from the DAC if the ADC is in phase. Interestingly, if I change the ADC frequency from 0 and then back to 0, the response picks up an imaginary components as well. So this seems to confirm that changing frequency on the ADC generates a relative phase between the DAC and ADC which I am not accounting for.

In the experiment we are running, we need to collect phase data over a frequency spectrum. Since this problem might be a difficult fix, do you have any other suggestions for collecting phase data with the RFSoC2x2?

Thanks again!

I don’t think this is necessarily an RFSoC2x2 question, but a general Software Defined Radio (SDR) problem. There’s an example in this paper, where an SDR was used to get a frequency magnitude and phase responses, and the phase graph produced was very similar to what you posted. I assume this is happening because you’re transmitting over a real channel and reconfiguring transmitter parameters always gives you a change of phase.

Because this might not be strictly an RFSoC2x2 problem, you might be able to expand your search for answers in more broader communities. I would look around to see if you can find someone doing this with any real world transmissions using SDRs, such as a USRP or BladeRF, or anything running GNU Radio, the answers from that would be directly applicable to what you are trying to do on the RFSoC.


Hi Shawn,

I will look into the SDRs and see if I can find anything on this. In the meantime, I was wondering if maybe there’s a way to sync up the ADC and DAC mixers within the base overlay. In particular, do you know of any resources that explain the parameters in the QMC and Mixer Settings such as EventSource and PhaseOffset? The documentation on what exactly these settings do has been tough to find.

Thank you!

Hi Tommy,

I agree the material is quite sparse… Best place would probably be the official IP manuals like the RFDC manual, if you look for the string “xrfdc_event” you will find something relevant hopefully.

If you feel comfortable reading code, then you can check out the rfdc driver code. This is what the pynq xrdc package python bindings are based on.


1 Like