0
$\begingroup$

I am trying to simulate an IQ mixer by multiplying two periodic signals IF and LO.


Edit:

My signals are given by $s_{1} = A_{1}e^{j\omega_{1}t}$ and $s_{2} = A_{2}e^{j\omega_{2}t}$ and they are being multiplied to produce $s_{3} = A_{1}A_{2}e^{j\omega_{1}t}e^{j\omega_{2}t}$.


I expect the power of their product to be the sum of their powers in dbm, because $10log_{10}(A_{1}^2 \cdot A_{2}^2) = 10log_{10}(A_{1}^2) + 10log_{10}(A_{2}^2)$.

Thus, I would expect the residual to be zero in the example at the end of this question, but instead it returns

[False False False False False False False False False False]
[31.50514998 31.50514998 31.50514998 31.50514998 31.50514998 31.50514998 31.50514998 31.50514998 31.50514998 31.50514998]

My question:

I can just scale the product to have any power I like, but I would like to know why the power doesn't add as I had expected. What am I overlooking?

The example:

"""

An example script to illustrate the fact that the product of
IF and LO isn't the same as the sum of their powers in dbm.

"""

import numpy as np


def lin_in_dbm(input):
    """

    Args:
        input: a scalar value

    Returns: the input value in dbm

    """

    return 10 * np.log10(input) - 30


def dbm_in_lin(input):
    """

    Args:
        input: a value in dbm

    Returns: the input value on a linear scale

    """

    return 10**((input - 30) / 10)


IF_freq = 4 * 10**7
LO_freq = 5 * IF_freq

simulation_sample_rate = 2 * max(IF_freq, LO_freq)
simulation_period = 1 / min(IF_freq, LO_freq)
simulation_samples = int(np.ceil(simulation_period * simulation_sample_rate))
time = np.linspace(0, simulation_samples, simulation_samples) * (simulation_period / simulation_samples)

IF_amplitude = dbm_in_lin(1)
IF = IF_amplitude * np.exp(1j * 2 * np.pi * IF_freq * time)

LO_amplitude = dbm_in_lin(1)
LO = LO_amplitude * np.exp(1j * 2 * np.pi * LO_freq * time)


product = IF * LO

IF_power_dbm = lin_in_dbm(np.sqrt((IF.real**2 + IF.imag**2) / 2))
LO_power_dbm = lin_in_dbm(np.sqrt((LO.real**2 + LO.imag**2) / 2))
product_power_dbm = lin_in_dbm(np.sqrt((product.real**2 + product.imag**2) / 2))

residual = product_power_dbm - (IF_power_dbm + LO_power_dbm)

print(residual == 0)
print(residual)



Edit:

Thanks to Dan Boschen's answer, I fixed the function "lin_in_dbm" and I noticed that I was trying to convert the amplitudes to dbm without squaring them.

The error (residual) is $-30~dbm$ now. Since it still isn't zero, there must still be a problem that I am missing. Any ideas?

Here is the updated code

"""

An example script to illustrate the fact that the product of
IF and LO isn't the same as the sum of their powers in dbm.

"""

import numpy as np
import sys as sys
import doctest


def lin_in_dbm(inp):
    """

    Args:
        inp: power in watt

    Returns: power in dbm

    >>> lin_in_dbm(np.linspace(1, 1, 1))
    array([30.])

    >>> lin_in_dbm(np.linspace(10, 10, 1))
    array([40.])


    """

    if inp.all() > sys.float_info.min:
        return 10 * np.log10(inp) + 30
    elif inp.all() == 0:
        return 10 * np.log10(sys.float_info.min) + 30
    else:
        raise Exception('The inp must be a positive number.')


def dbm_in_lin(inp):
    """

    Args:
        inp: power in dbm

    Returns: power in watt

    >>> dbm_in_lin(np.linspace(30, 30, 1))
    array([1.])

    >>> dbm_in_lin(np.linspace(40, 40, 1))
    array([10.])

    """

    return 10**((inp - 30) / 10)


IF_freq = 4 * 10**7
LO_freq = 5 * IF_freq

simulation_sample_rate = 2 * max(IF_freq, LO_freq)
simulation_period = 1 / min(IF_freq, LO_freq)
simulation_samples = int(np.ceil(simulation_period * simulation_sample_rate))
time = np.linspace(0, simulation_samples, simulation_samples) * (simulation_period / simulation_samples)

IF_amplitude = 1
IF = IF_amplitude * np.exp(1j * 2 * np.pi * IF_freq * time)

LO_amplitude = 1
LO = LO_amplitude * np.exp(1j * 2 * np.pi * LO_freq * time)


product = IF * LO

IF_amplitude = np.abs(IF)
LO_amplitude = np.abs(LO)
product_amplitude = np.abs(product)

IF_power_watt = IF_amplitude**2
LO_power_watt = LO_amplitude**2
product_power_watt = product_amplitude**2

IF_power_dbm = lin_in_dbm(IF_power_watt)
LO_power_dbm = lin_in_dbm(LO_power_watt)
product_power_dbm = lin_in_dbm(product_power_watt)

error = product_power_dbm - IF_power_dbm - LO_power_dbm

print(error == 0)
print(error)
print(dbm_in_lin(error))

doctest.testmod()


$\endgroup$
2
  • $\begingroup$ You do not say how you are mixing the signal. Are you just multiplying, i.e. $x_{out} = x_{LO} x_{RF}$? $\endgroup$ – TimWescott May 17 '20 at 1:21
  • $\begingroup$ Yes I am just multiplying the two signals $\endgroup$ – Chandran Goodchild May 17 '20 at 12:59
2
$\begingroup$

From the code example the OP is trying to show the result for the product $s_1s_2$

with $s_1=A_1e^{j\omega_1t}$ and $s_2=A_2e^{j\omega_2t}$

The product is given as:

$$s_3= A_1e^{j\omega_1t}A_2e^{j\omega_2t} = A_1A_2e^{j(\omega_1+\omega_2)t}$$.

The power of $s_1$ is $A_1^2$, the power of $s_2$ is $A_2^2$, and the power of the product is $(A_1A_2)^2$ (Which would be in Watts if the units were in volts and with a 1 ohm load, but generally the conjugate product is considered the power).

Certainly the power would add in dB in this case as the OP had expected. In dB:

$$P_{s1dB}= 20Log_{10}(A_1)$$ $$P_{s2dB}= 20Log_{10}(A_2)$$

$$P_{s3dB} =20Log_{10}(A_1A_2) = 20Log_{10}(A_1)+20Log_{10}(A_2)$$

Where the above would be dBW, dB relative to 1W, if the input units squared were in units of Watts. The power as represented in dBm is with the power referenced to 1 mW, given as follows:

$$P_{s1dBm}= P_{s1dB}-{P_{dB}(1mW)} = 20Log_{10}(A_1)+30$$ $$P_{s2dBm}= 20Log_{10}(A_2)+30$$

Similarly following the same approach, the product in dBm would be:

$$P_{s3dBm} =20Log_{10}(A_1A_2)+30 = 20Log_{10}(A_1)+20Log_{10}(A_2)+30 $$

For the case of the OP's example with $A_1=1$ and $A_2=1$ is below:

Using approach above: $P_{s3dBm} = 20Log_{10}(1x1) +30= 30 =20Log_{10}(1) + 20Log_{10}(1) + 30$

The reason the OP is not getting this result is because 1 W x1 W = 1 W, but 1000 mW x 1000 mW is not 1e6 mW!


An additional issue with the first version of the OP's code was the "lin_in_dbm" and "dbm_in_lin" functions as they are not symmetrical. dbm_in_lin(30) returns 1 which is consistent with 30 dBm = 1W, thus confirming "lin" is in units of watts for an input in dBm. However lin_in_dbm(1) returns -30.0 instead of +30 as expected and is corrected as follows:

The function lin_in_dBm should return:

10 * np.log10(input) + 30

$\endgroup$
2
  • 1
    $\begingroup$ @Chandran I believe I found your final issue and updated my answer $\endgroup$ – Dan Boschen May 17 '20 at 14:20
  • $\begingroup$ Thank you! This was certainly the problem... $\endgroup$ – Chandran Goodchild May 17 '20 at 16:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.