DSPRelated.com
Forums

Complex IF

Started by maxplus 3 years ago20 replieslatest reply 3 years ago289 views

Hello

I am building up a RF transceiver using integrated up/down converter chip.

This particular chip requires a complex Intermediate Frequency as inputs for up conversion (Tx-IF-I, Tx-IF-Q).  As such an external 3dB splitter/hybrid combiner e.g. Mini Circuit QCH-63+ is used to convert real IF to complex IF (I+jQ). 

Due to the physical implementation, the 

0-degree pin of the combiner is facing the Tx-IF-Q (up converter)

and the 90-degree pin/combiner is facing Tx-IF-I (up converter).

To avoid cross-over, is it ok to connect as they are. Will the upconverted waveform shift?

0-degree/combiner > Tx-IF-Q (up converter)

90-Degree/combiner > Tx-IF-I (up converter)

Also, how do I evaluate EVM due to gain/phase imbalances due to combiner and imperfection in the modulator. 

I am just curious why the modulator cannot just accept real IF and for the complex conversion inside the chip.


[ - ]
Reply by drmikeSeptember 19, 2021

The purpose I & Q is to keep track of phase.  If you swap real and imaginary you just twist the phase by 90 degrees.  The amplitude is still the same.  The measurements you get out will still be the same, you have just "renamed" real to be imaginary and vice versa.  Since phase is Atan(imgaginary/real) you have the inverse - the Atan(real/imaginary) = 1/Atan(imaginary/real).   If you really care about the input phase of the signal, you can still find what it is.

As for why they use complex input and output, it's easier for them for one thing, and for another they don't know what is important to you as the user for the conversion to the IF.  Too many variables they don't want to deal with.

Hook it up and play - If things look upside down it's because they are!

[ - ]
Reply by maxplusSeptember 19, 2021

Hello

I done some calculations just to prove for myself but the angles calculated are different. 

Real=0.5. Imag=0.9

Phase = arctan (0.9/0.5) = 60.9deg

now if I inverse due to the physical implementation 

Phase= arctan (0.5/0.9) = 29.05deg.




. I.e. the angles haven’t twisted by 90 deg. Also, how do I assess EVM due to gain and phase imbalances. Thank you
[ - ]
Reply by CharlieRaderSeptember 19, 2021

Your angles 60.9 deg and 29.05 deg add up to 90 deg within a probably rounding error. The phase has not been "twisted" but reflected.

[ - ]
Reply by drmikeSeptember 19, 2021

Exactly!  $atan(x)=\pi - atan(1/x)$  So the term "twisted" implies a positive rather than negative shift.  "Reflected" is a better term.

The gain in the EVM should be the same.  The phase imbalances will be reflected by 90 degrees.  Since you have a "real" signal for input, the phase doesn't matter so much.  However, if there is a phase shift that is different in each arm (I or Q) then you will get different results from swapping the inputs.  If you are operating in the GHz range it would be hard to do the experiment of swapping the inputs to check.  If you can, do that experiment of swapping the I and Q so you can see what happens and understand the math behind it.  It won't be exactly the same, but it should be close.  Unless there's a problem, and that tells you something too.

[ - ]
Reply by CharlieRaderSeptember 19, 2021
$atan(x)=\pi/2 - atan(1/x)$
[ - ]
Reply by LeonardDieguezSeptember 19, 2021

The real signal can be thought of as if there are 2 complex signals one with a constellation point rotating in a positive counter-clock wise rotation and one with a negative clock wise rotation, both at the same rads/sec but in equal and opposite directions so they cancel the imaginary part out. selecting the IQ pairs that go into the up-converter will select one of these signals. I think of it as one being the upper side band and the other the lower side band, with respect to the carrier. You can simulate this in matlab or python by just encoding a tone then up-converting it. Either by multiplying your complex signal by -i,i or through hilbert transform you can rotate the signal to see some of the affects.

[ - ]
Reply by maxplusSeptember 19, 2021

Many thanks for the explanation. 

Do I take it the overall waveform would be the same on the spectrum analyser (upconverted signal at f1, image)?

For Rx down-conversion, I have to convert the complex IF back to real IF.

Do I ensure that I (ftom down-conversion is fed to Q of combiner and Q (from downconversion) to I?

Can someone point to how I implement this on Matlab or Python?

Thank you 

[ - ]
Reply by kazSeptember 19, 2021

This matlab code can help modelling your thoughts:

clc;clear;

x = randn(1,2^10);

x = hilbert(x);

fup = exp(j*2*pi*(0:length(x)-1)*.3);

xup = x.*fup;

fdn = exp(j*2*pi*(0:length(x)-1)*-.3);

xdn = xup.*fdn;

plot(real(x));hold;

plot(real(xdn),'g--');

%


[ - ]
Reply by kazSeptember 19, 2021

Post deleted by author

[ - ]
Reply by dgshaw6September 19, 2021

Many excellent answers here, but 1 item I think has not been mentioned.
The word reflection is used in several of them, and that is the essence of my comment.
An exchange of I and Q is in fact a CONJUGATE with a 90 degree phase rotation.

As mentioned, you probably don't care about the absolute phase, so the 90 degree rotation probably does not matter.

However, it is not clear to me whether that conjugate makes a difference for you.

[ - ]
Reply by maxplusSeptember 19, 2021

I read one of the message that swapping I and Q (like in the case described earlier)effectively spectrally invert the waveform. 

Swapping I and Q reverses all the frequencies. For example, a signal 5 kHz above the mixer's LO will appear at -5 kHz, instead of 5kHz

https://www.dsprelated.com/showthread/comp.dsp/550...

My concern would I be able to see the up converted signal on the spectrum analyser as the waveform us spectrally inverted in negative domain. 

[ - ]
Reply by dgshaw6September 19, 2021

I'm not sure that I can accurately visualize the issue you are facing, but conjugation is exactly the same as rotating in reverse.

exp(-1j*2pi*fc) vs. exp(+1j*2pi*fc)

[ - ]
Reply by LeonardDieguezSeptember 19, 2021

Please excuse the long msg but a picture paints a 1000 words (really 1024 to be exact!) I'm including code below not sure now to make it show up in a separate window. In the code I have created a msg it is nothing more than a 1MHz sine wave. lets call it the IF signal. If we take the real part and perform a PSD on it it looks like this

(1) psd of xmsg_62954.png

If we take a hilbert transform of the msg it and display it 

(2) psd of complex xmsg_28144.png

taking the real parts of the carrier and msg mixing them you get this:

Note the positive and negative images for both the carrier and the up-converted msg


(6) psd of signal xmsg x carrier_70492.p

OK now for the punch line, here is the complex upconvert. 

(7) psd of complex up convert_87550.png

101MHz as expected. So taking the real and imaginary parts and swapping them produces this:

(8) psd with xmsg iq reversed_39355.png


Not sure if this helps but nice to look at some diagrams. Here is the code in python if you want to reproduce it. 


import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import hilbert


def plot_data(data, names, points=False):
    """
    function plots n rows of data based on list length

    data : list of data to plot
    names : list of names of data
    title : plot name and file name.
    """
    if len(data) != len(names):
        print('list of data and names are not equal length')
        return
    else:
        fig, ax = plt.subplots(len(data), 1)  # N by 1 subplots

        title = ' '.join(names)
        # plt.title(title)

        for i in range(len(data)):
            ax[i].plot(data[i])
            if points:
                ax[i].plot(data[i], 'r.')
            ax[i].grid('on')
            # ax[i].legend()
            ax[i].set_ylabel(names[i])

        plt.show(block=False)
        plt.savefig(title+'.png')

def plot_iq_data2x(data1, data2, name1, name2):
        """
        function plots 2 rows of data, 1 for real, and 1 for imaginary
        data1,2 : complex data to plot
        """
        fig, (ax1, ax2) = plt.subplots(2, 1)
        ax1.plot(np.real(data1), 'b', label='real')
        ax1.plot(np.real(data1), 'r.')
        ax1.plot(np.imag(data1), 'g', label='imag')
        ax1.plot(np.imag(data1), 'r.')
        ax1.grid('on')
        ax1.legend()
        ax2.plot(np.real(data2), 'b', label='real')
        ax2.plot(np.real(data2), 'r.')
        ax2.plot(np.imag(data2), 'g', label='imag')
        ax2.plot(np.imag(data2), 'r.')
        ax2.grid('on')
        ax2.legend()
        ax1.set_ylabel(name1)
        ax2.set_ylabel(name2)
        # ax1.set_xlim([-Tb, 4 * Tb])
        # ax2.set_xlim([-Tb, 4 * Tb])
        plt.legend()
        plt.show(block=False)
        plt.savefig(name1+'_'+name2+'.png')

def plot_psd(samples, Fs, plt_title, xlim=None):
    # Calculate power spectral density (frequency domain version of signal)
    psd = np.abs(np.fft.fftshift(np.fft.fft(samples))) ** 2
    psd_dB = 10 * np.log10(psd)
    freqs = np.linspace(Fs/-2, Fs/2, len(psd))

    # Plot freq domain
    plt.figure()
    plt.plot(freqs, psd_dB)
    plt.xlabel("Frequency [MHz]")
    plt.ylabel("PSD")
    if xlim:
        plt.xlim(xlim)
    plt.ylim([0, np.max(psd_dB)+10])
    plt.grid('on')
    plt.title(plt_title)
    plt.show(block=False)
    plt.savefig(plt_title+'.png')

def plot_iq_psd(x, fs, name):
    zoom_len = int(len(x)/10)
    plot_iq_data2x(x, x[0:zoom_len], name, 'zoom')
    psd_name = name + '_' +str(fs)+'_sample_rate'
    plot_psd(x, fs, psd_name)
    plt.savefig(name+'.png')

# ---- start here --------
if __name__ == '__main__':
    # make your over sample rate based on how many times a cycle you want a data point for viewing.
    # note all numbers assume to be in MHz
    Fc = 100  # carrier freq set to 100MHz
    N = 2**16  # number of IF cycles to simulate
    os = 16   # number of samples per cycle
    Fs = os * Fc
    Ts = 1 / Fs

    # generate your time base starting at time zero
    t = np.arange(0, os*N) / Fs

    # generate the message, for here you are just using a tone at Fmsg
    Fmsg = 1  # generate a 1 MHz tone
    xmsg = np.cos(2 * np.pi * Fmsg * t)
    plot_psd(xmsg[0::100], Fs/100, '(1) psd of xmsg')

    xmsg_cmplx = hilbert(xmsg)
    plot_psd(xmsg_cmplx[0::100], Fs / 100, '(2) psd of complex xmsg')
    plot_iq_psd(xmsg_cmplx[0:2**16:16], Fs/16, '(3) IQ of xmsg complex')

    # take the real parts of the msg and carrier and mix
    x0 = xmsg * np.sin(2 * np.pi * Fc * t)

    plot_data([x0[0:2048], xmsg[0:2048]], ['(5) real part x0', '(5)real part xmsg'], True)
    plot_psd(x0, Fs, '(6) psd of signal xmsg x carrier', xlim=[-110, 110])

    # complex mix
    x1 = xmsg_cmplx * (np.cos(2*np.pi*Fc*t) + 1j*np.sin(2*np.pi*Fc*t))
    plot_psd(x1, Fs, '(7) psd of complex up convert', xlim=[95,105])

    # now reverse IQ on the msg signal
    xmsg_rev = np.imag(xmsg_cmplx) + 1j*np.real(xmsg_cmplx)
    x2 = xmsg_rev * (np.cos(2*np.pi*Fc*t) + 1j*np.sin(2*np.pi*Fc*t))
    plot_psd(x2, Fs, '(8) psd with xmsg IQ reversed', xlim=[95, 105])

    print('end')

[ - ]
Reply by maxplusSeptember 19, 2021

Thank you for the code.

I like to  get a common understanding. 

I can see in the last graph that the spectrum have been inverted due to I and Q being reversed so 101MHz being reflected to 99MHz as a result. 

Why is it in Fig.6 that the spectrum is not 99MHz and +101MHz as a result of mixing 1MHz with 100MHz LO? Can you please clarify? 

[ - ]
Reply by LeonardDieguezSeptember 19, 2021

In Figure 6 it is a visualization of the real parts of each signal, the xmsg and the carrier multiplied together. On the positive side of the spectrum you have carrier +/- the msg  so 99, 101 and on the negative side of the spectrum you have -99 and -101. If you run the code you can zoom into the figures. You may need to use a debugger and place a break point on the figure is plotted that you want to examine or change "block=False" to "block=True" on the figure you want the sim to stop on in the plt.show() statement.

(6) psd of signal xmsg x carrier_43039.p

(6b) psd of signal xmsg x carrier_54267.

[ - ]
Reply by maxplusSeptember 19, 2021

I get an error when I run the code in Spyder.

divide by zero encountered in log10 psd_dB = 10 * np.log10(psd)

[ - ]
Reply by maxplusSeptember 19, 2021

This is in reference to Python.

I also ran the Matlab code.  Does it just plot one graph (Fig.1) showing a complex waveform? 

[ - ]
Reply by LeonardDieguezSeptember 19, 2021

I'm using pycharm. For some reason spyder will not run on my machine. Possibly check the version of numpy and update it. I have had issues with old versions of numpy. 


In the IDE on the command line you could try just executing the commands you would need to make the array psd then pass it to the numpy function so numpy.log10()  after you import numpy.

[ - ]
Reply by LeonardDieguezSeptember 19, 2021

After uninstalling spyder and re-installing it Spyder is working again. The file seems to be running fine on my setup. Not sure what is going on.

[ - ]
Reply by fharrisSeptember 19, 2021

In a period a long time ago, we build modulators by adjusting the amplitude, phase, or frequency of a an analog carrier directly. We don't do that anymore! what we now do is build a complex baseband waveform in the sampled data domain using DSP in various integrated circuits. The sampled data signal is then converted to an analog baseband signal by a pair of A-to-D converters and analog smoothing filters. This signal is translated to an intermediate (or RF) frequency by the real part of the product [I(t)+jQ(t)] times [cos(2 pi fc t) +jsin(2 pi fc t)] = I(t) cos(2 pi fc t) - Q(t) sin(2 pi fc t)].


fred h