Complex Example

Here we have a more complex example

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import rf_linkbudget as rf
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd


def example():

    cr = rf.Circuit('Example')

    dup = rf.Attenuator("Duplexer",
                        Att=[1.5])

    lim = rf.Attenuator("Limiter",
                        Att=np.array([0.5, 6.5, 12.5, 18.5]),
                        IIP3=60,
                        OP1dB=30)

    sw1 = rf.SPDT("SW 1",
                  Att=0.3)

    lna = rf.Amplifier("LNA",
                       Gain=[(0, 18.2)],
                       NF=0.7,
                       OP1dB=21.5,
                       OIP3=40)

    sw2 = rf.SPDT("SW 2",
                  Att=0.3)

    att_fix = rf.Attenuator("Att Fix1",
                            Att=[1.5])

    rxfilt = rf.Attenuator("Rx Filter",
                           Att=[2.0])

    att_fix2 = rf.Attenuator("Att Fix2",
                             Att=[1.5])

    driver = rf.Amplifier("Driver",
                          Gain=[(0, 14.9)],
                          NF=1.7,
                          OP1dB=21.4,
                          OIP3=40)

    dsa = rf.Attenuator("DSA",
                        Att=np.arange(1.0, 29, 1.0))

    adc = rf.Amplifier("ADC",
                       Gain=[(0, 0)],
                       OP1dB=-1,
                       OIP3=34,
                       NF=19)

    src = rf.Source("Source")
    sink = rf.Sink("Sink")

    src['out'] >> dup['in']
    dup['out'] >> lim['in']
    lim['out'] >> sw1['S']

    sw1['S-1'] >> lna['in']
    lna['out'] >> sw2['S-1']
    sw1['S-2'] >> sw2['S-2']

    sw2['S'] >> att_fix['in']
    att_fix['out'] >> rxfilt['in']
    rxfilt['out'] >> att_fix2['in']
    att_fix2['out'] >> driver['in']
    driver['out'] >> dsa['in']
    dsa['out'] >> adc['in']
    adc['out'] >> sink['in']

    # create callback function
    def cb_src(self, f, p):
        return {'f': f, 'p': p, 'Tn': rf.RFMath.T0}

    src['out'].regCallback(cb_src)

    # create callback function
    def cb_lim(self, f, p):
        tb = {-31: 6, -30: 6, -29: 6, -28: 6, -27: 6, -26: 6, -25: 12, -24: 12, -23: 12, -22: 12, -21: 12, -20: 12, -19: 18, -18: 18, -17: 18, -16: 18, -15: 18, -14: 18, -13: 18, -12: 18, -11: 18, -10: 18,}
        if(p in tb):
            self.setAttenuation(tb[p])
        else:
            self.setAttenuation(0.0)
        return {}

    lim['in'].regCallback(cb_lim)

    # create callback function
    def cb_dsa(self, f, p):
        tb = {-37: 0, -36: 1, -35: 2, -34: 3, -33: 4, -32: 5, -31: 0, -30: 1, -29: 2, -28: 3, -27: 4, -26: 5, -25: 0, -24: 1, -23: 2, -22: 3, -21: 4, -20: 5, -19: 0, -18: 1, -17: 2, -16: 3, -15: 4, -14: 5, -13: 6, -12: 7, -11: 8, -10: 9,}
        if(p in tb):
            self.setAttenuation(tb[p])
        else:
            self.setAttenuation(0)
        return {}

    dsa['in'].regCallback(cb_dsa)

    # create callback function
    def cb_sw1(self, f, p):
        if(p > -11):
            self.setDirection('S-2')
        else:
            self.setDirection('S-1')
        return {}

    sw1['S'].regCallback(cb_sw1)
    sw2['S'].regCallback(cb_sw1)

    cr.finalise()

    return cr


if __name__ == "__main__":

    # define circuit
    cr1 = example()

    # simualte
    sim1 = cr1.simulate(network=cr1.net,
                        start=cr1['Source'],
                        end=cr1['Sink'],
                        freq=[0],
                        power=np.arange(-50, -10, 1.0))

    # key's of interest
    # only these keys show up in the plots
    keys1 = [cr1['Source']['out'],
             cr1['Duplexer']['out'],
             cr1['Limiter']['out'],
             cr1['SW 1']['S'],
             cr1['SW 1']['S-1'],
             cr1['LNA']['out'],
             cr1['SW 2']['S'],
             cr1['Att Fix1']['out'],
             cr1['Rx Filter']['out'],
             cr1['Att Fix2']['out'],
             cr1['Driver']['out'],
             cr1['DSA']['out'],
             cr1['ADC']['out'],
             cr1['Sink']['in']]

    # set noise bandwidth to smallest subband
    sim1.setNoiseBandwidth(15e3)

    # plot system parameter
    h = sim1.plot_chain(keys=keys1)
    k = sim1.plot_total(['NF', 'DYN'])
    t = sim1.plot_total_simple(['SFDR', 'SNR'], freq=0)
    k = sim1.plot_total(['NF', 'DYN'])

With the plot_total function we can show the NoiseFigure and the Dynamic of the system.

(Source code, png, hires.png, pdf)

_images/example4.png

(Source code, png, hires.png, pdf)

_images/example5.png

We see steps in the noisefigure over the input power level. We see that these steps are from the limiter attenuator called lim / Limiter. The limiter is in front of the LNA which is not optimal.

We also see that the overall dynamic has losses exactly at the same input levels. Which is not surprising.

(Source code, png, hires.png, pdf)

_images/example6.png

When we compare the SNR and the SFDR view in a plot_total_simple we see something interesting. We see the sweet spot between SNR and the spurious free dynamic range. To have an optimal system means we have to optimize the circuit in a way, so that SFDR and SNR are equal over the hole input power range.

SNR / SFDR optimisation

To optimize the SNR / SFDR we have to keep the signal power of each component in view.

(Source code, png, hires.png, pdf)

_images/example8.png

Here in this plot we got an input power of -32dBm. The Driver Amplifier is at an output power of approx -6dBm. And the ADC level is approx at -12dBm which leads to approx -13dBFS. But already with the next higher input power level we have to adjust the ADC input level, otherwise the IM3 components will dominate the hole dynamic. At -31dBm the limiter was activated and will attenuate approx 6.5dB. We see this in the other figures as the krack in the performance. But it is necessary. Otherwise the spurious would dominate and our signal would have an overall worse dynamic.

The End

Thats it for now.
Have fun with this python package!