Example #1
0
def l1norm(H):
    """Compute the l1-norm of a z-domain transfer function.

        The norm is evaluated over the first 100 samples.

        **Parameters:**

        H : sequence or lti object
            Any supported LTI representation is accepted.

        **Returns:**

        l1normH : float
            The L1 norm of ``H``.

        .. note:
            LTI objects are translated to ZPK tuples, with possible
            rounding errors.

    """
    if _is_zpk(H):
        z, p, k = H
        HP = (z, p, k, 1.)
    elif _is_num_den(H):
        num, den = H
        HP = (num, den, 1.)
    elif _is_A_B_C_D(H):
        A, B, C, D = H
        HP = (A, B, C, D, 1.)
    elif isinstance(H, lti):
        warn('l1norm() got an LTI object, translated to zpk form, rounding errors possible.')
        z, p, k = _get_zpk(H)
        HP = (z, p, k, 1.)
    _, y = dimpulse(HP, t=np.arange(100))
    return np.sum(np.abs(y[0]))
Example #2
0
    def test_more_step_and_impulse(self):
        lambda1 = 0.5
        lambda2 = 0.75
        a = np.array([[lambda1, 0.0],
                      [0.0, lambda2]])
        b = np.array([[1.0, 0.0],
                      [0.0, 1.0]])
        c = np.array([[1.0, 1.0]])
        d = np.array([[0.0, 0.0]])

        n = 10

        # Check a step response.
        ts, ys = dstep((a, b, c, d, 1), n=n)

        # Create the exact step response.
        stp0 = (1.0 / (1 - lambda1)) * (1.0 - lambda1 ** np.arange(n))
        stp1 = (1.0 / (1 - lambda2)) * (1.0 - lambda2 ** np.arange(n))

        assert_allclose(ys[0][:, 0], stp0)
        assert_allclose(ys[1][:, 0], stp1)

        # Check an impulse response with an initial condition.
        x0 = np.array([1.0, 1.0])
        ti, yi = dimpulse((a, b, c, d, 1), n=n, x0=x0)

        # Create the exact impulse response.
        imp = (np.array([lambda1, lambda2]) **
                            np.arange(-1, n + 1).reshape(-1, 1))
        imp[0, :] = 0.0
        # Analytical solution to impulse response
        y0 = imp[:n, 0] + np.dot(imp[1:n + 1, :], x0)
        y1 = imp[:n, 1] + np.dot(imp[1:n + 1, :], x0)

        assert_allclose(yi[0][:, 0], y0)
        assert_allclose(yi[1][:, 0], y1)

        # Check that dt=0.1, n=3 gives 3 time values.
        system = ([1.0], [1.0, -0.5], 0.1)
        t, (y,) = dstep(system, n=3)
        assert_allclose(t, [0, 0.1, 0.2])
        assert_array_equal(y.T, [[0, 1.0, 1.5]])
        t, (y,) = dimpulse(system, n=3)
        assert_allclose(t, [0, 0.1, 0.2])
        assert_array_equal(y.T, [[0, 1, 0.5]])
Example #3
0
 def impulse_response(self, impulse_length=30):
     """
     Get the impulse response corresponding to our model.  Returns psi,
     where psi[j] is the response at lag j.  Note: psi[0] is unity.
     """        
     sys = self.ma_poly, self.ar_poly, 1 
     times, psi = dimpulse(sys, n=impulse_length)
     psi = psi[0].flatten()  # Simplify return value into flat array
     return psi
Example #4
0
def dsclansObjb(x, order, OSR, Q, rmax, Hz):
    """Constraint function for clans; 
    g =||h||_1 - Q
    """
    H = dsclansNTF(x, order, rmax, Hz)
    H = (H[0], H[1], H[2], 1.)
    # dimpulse(H, n=100)[y is 0][output 0]
    g = np.sum(np.abs(dimpulse(H, t=np.arange(100))[1][0])) - 1 - Q
    return -g
Example #5
0
    def test_dimpulse(self):

        a = np.asarray([[0.9, 0.1], [-0.2, 0.9]])
        b = np.asarray([[0.4, 0.1, -0.1], [0.0, 0.05, 0.0]])
        c = np.asarray([[0.1, 0.3]])
        d = np.asarray([[0.0, -0.1, 0.0]])
        dt = 0.5

        # Because b.shape[1] == 3, dimpulse should result in a tuple of three
        # result vectors
        yout_imp_truth = (np.asarray([0.0, 0.04, 0.012, -0.0116, -0.03084,
                                      -0.045884, -0.056994, -0.06450548,
                                      -0.068804844, -0.0703091708]),
                          np.asarray([-0.1, 0.025, 0.017, 0.00985, 0.00362,
                                      -0.0016595, -0.0059917, -0.009407675,
                                      -0.011960704, -0.01372089695]),
                          np.asarray([0.0, -0.01, -0.003, 0.0029, 0.00771,
                                      0.011471, 0.0142485, 0.01612637,
                                      0.017201211, 0.0175772927]))

        tout, yout = dimpulse((a, b, c, d, dt), n=10)

        assert_equal(len(yout), 3)

        for i in range(0, len(yout)):
            assert_equal(yout[i].shape[0], 10)
            assert_array_almost_equal(yout[i].flatten(), yout_imp_truth[i])

        # Check that the other two inputs (tf, zpk) will work as well
        tfin = ([1.0], [1.0, 1.0], 0.5)
        yout_tfimpulse = np.asarray([0.0, 1.0, -1.0])
        tout, yout = dimpulse(tfin, n=3)
        assert_equal(len(yout), 1)
        assert_array_almost_equal(yout[0].flatten(), yout_tfimpulse)

        zpkin = tf2zpk(tfin[0], tfin[1]) + (0.5,)
        tout, yout = dimpulse(zpkin, n=3)
        assert_equal(len(yout), 1)
        assert_array_almost_equal(yout[0].flatten(), yout_tfimpulse)

        # Raise an error for continuous-time systems
        system = lti([1], [1, 1])
        assert_raises(AttributeError, dimpulse, system)
Example #6
0
    def impulse_response(self, n=100):
        """
        Calculates the impulse response of the filter (n steps, default = 100)
        """
        impulse = [1]
        for number in range(1, n):
            impulse.append(0)

        _times, imp_resp = signal.dimpulse((self.b, self.a, 1), n=n)
        impulse_response = imp_resp[0].flatten()
        return impulse, impulse_response
Example #7
0
    def impulse_response(self, impulse_length=30):
        """
        Get the impulse response corresponding to our model.
        Returns
        -------
        psi : array_like(float)
            psi[j] is the response at lag j of the impulse response.
            We take psi[0] as unity.
        """
        sys = self.ma_poly, self.ar_poly, 1
        times, psi = dimpulse(sys, n=impulse_length)
        psi = psi[0].flatten()  # Simplify return value into flat array

        return psi
def powerGain(num, den, Nimp=100):
    """Calculate the power gain of a TF given in coefficient form.

    Nimp is the recommended number of impulse response samples for use
    in future calls and Nimp0 is the suggested number (100) to use.
    """
    unstable = False
    _, (imp, ) = dimpulse((num, den, 1), t=np.linspace(0, Nimp, Nimp))
    if np.sum(abs(imp[Nimp - 11:Nimp])) < 1e-08 and Nimp > 50:
        Nimp = np.round(Nimp/1.3)
    else:
        while np.sum(abs(imp[Nimp - 11:Nimp])) > 1e-06:
            Nimp = Nimp*2
            _, (imp, ) = dimpulse((num, den, 1), t=np.linspace(0, Nimp, Nimp))
            if np.sum(abs(imp[Nimp - 11:Nimp])) >= 50 or Nimp >= 10000.0:
                unstable = True
                break

    if not unstable:
        pGain = np.sum(imp**2)
    else:
        pGain = np.Inf

    return pGain, Nimp
Example #9
0
def impL1(arg1, n=10):
    """Impulse response evaluation for NTFs.

    Compute the impulse response from the comparator
    output to the comparator input for the given NTF.

    **Parameters:**

    arg1 : object
        The NTF, which may be represented as:

        * ZPK tuple,
        * num, den tuple,
        * A, B, C, D tuple,
        * ABCD matrix,
        * a scipy LTI object,
        * a sequence of the tuples of any of the above types (experimental).

    n : int
        is the (optional) number of time steps (default: 10), resulting in
        an impulse response with n+1 (default: 11) samples.

    This function is useful when verifying the realization
    of a NTF with a specified topology.

    **Returns:**

    y : ndarray
        The NTF impulse response

    .. note::

        In the original implementation of impL1 in delsig, there is a
        bug: impL1 calls MATLAB's impulse with tfinal=n, which means that
        the function will return the impulse response evaluated on the times
        [0, 1, 2 ... n], ie n+1 points. We keep the same behavior here,
        but we state clearly that n is the number of time steps.

    """
    if _is_num_den(arg1):
        num, den = arg1
        p = np.roots(den)
    elif _is_zpk(arg1):
        z, p, _ = arg1
        num = np.poly(z)
        den = np.poly(p)
    else:
        num, den = _get_num_den(arg1)
        p = np.roots(den)

    num = np.asarray(num)
    den = np.asarray(den)
    p = np.asarray(p)

    lf_den = padr(num, len(p)+1)
    lf_num = lf_den - den
    ts = np.arange(n + 1) # be coherent with the original toolbox
    all_lf = np.concatenate((lf_num, lf_den), axis=1)
    lf_num, lf_den = lf_num.squeeze(), lf_den.squeeze()
    if not np.allclose(np.imag(all_lf), np.zeros(all_lf.shape), atol=1e-9):
        # Complex loop filter
        lfr_den = np.real(convolve(lf_den, np.conj(lf_den))).squeeze()
        lfr_num = convolve(lf_num, np.conj(lf_den)).squeeze()
        lf_i = (np.real(lfr_num).tolist(), lfr_den.tolist(), 1)
        lf_q = (np.imag(lfr_num).tolist(), lfr_den.tolist(), 1)
        y = dimpulse(lf_i, t=ts)[1][0] + 1j*dimpulse(lf_q, t=ts)[1][0]
        y = y.squeeze()
    else:
        _, y = dimpulse((lf_num, lf_den, 1), t=ts)
        y = y[0].squeeze()
    return y
def realizeNTF_ct(ntf, form='FB', tdac=(0, 1), ordering=None, bp=None,
                  ABCDc=None, method='LOOP'):
    """Realize an NTF with a continuous-time loop filter.

    **Parameters:**

    ntf : object
        A noise transfer function (NTF).

    form : str, optional
        A string specifying the topology of the loop filter.

         * 'FB': Feedback form,
         * 'FF': Feedforward form

        For the FB structure, the elements of ``Bc`` are calculated
        so that the sampled pulse response matches the L1 impulse
        response.  For the FF structure, ``Cc`` is calculated.

    tdac : sequence, optional
        The timing for the feedback DAC(s). If ``tdac[0] >= 1``,
        direct feedback terms are added to the quantizer.

        Multiple timings (one or more per integrator) for the FB
        topology can be specified by making tdac a list of lists,
        e.g. ``tdac = [[1, 2], [1, 2], [[0.5, 1], [1, 1.5]], []]``

        In this example, the first two integrators have
        DACs with ``[1, 2]`` timing, the third has a pair of
        DACs, one with ``[0.5, 1]`` timing and the other with
        ``[1, 1.5]`` timing, and there is no direct feedback
        DAC to the quantizer.

    ordering : sequence, optional
        A vector specifying which NTF zero-pair to use in each resonator
        Default is for the zero-pairs to be used in the order specified
        in the NTF.

    bp : sequence, optional
        A vector specifying which resonator sections are bandpass.
        The default (``zeros(...)``) is for all sections to be lowpass.

    ABCDc : ndarray, optional
        The loop filter structure, in state-space form.
        If this argument is omitted, ABCDc is constructed according
        to "form."

    method : str, optional
        The default fitting method is ``'LOOP'``, which means that
        the DT and CT loop responses will be matched.
        Alternatively, it is possible to set the method to ``'NTF'``,
        which will result in the NTF responses to be matched.
        See :ref:`discrete-time-to-continuous-time-mapping` for a
        more in-depth discussion.

    **Returns:**

    ABCDc : ndarray
        A state-space description of the CT loop filter

    tdac2 : ndarray
        A matrix with the DAC timings, including ones
        that were automatically added.

    **Example:**

    Realize the NTF :math:`(1 - z^{-1})^2` with a CT system (cf with the
    example at :func:`mapCtoD`).::

        from deltasigma import *
        ntf = ([1, 1], [0, 0], 1)
        ABCDc, tdac2 = realizeNTF_ct(ntf, 'FB')

    Returns:

    ABCDc::

        [[ 0.          0.          1.         -1.        ]
         [ 1.          0.          0.         -1.49999999]
         [ 0.          1.          0.          0.        ]]

    tdac2::

        [[-1. -1.]
         [ 0.  1.]]

    """
    ntf_z, ntf_p, _ = _get_zpk(ntf)
    ntf_z = carray(ntf_z)
    ntf_p = carray(ntf_p)
    order = max(ntf_p.shape)
    order2 = int(np.floor(order/2.))
    odd = order - 2*order2
    # compensate for limited accuracy of zero calculation
    ntf_z[np.abs(ntf_z - 1) < eps**(1./(1. + order))] = 1.
    method = method.upper()
    if method not in ('LOOP', 'NTF'):
        raise ValueError('Unimplemented matching method %s.' % method)
    # check if multiple timings mode
    if (type(tdac) == list or type(tdac) == tuple) and len(tdac) and \
       (type(tdac[0]) == list or type(tdac[0]) == tuple):
        if len(tdac) != order + 1:
            msg = 'For multi-timing tdac, len(tdac) ' + \
                  ' must be order+1.'
            raise ValueError(msg)
        if form != 'FB':
            msg = "Currently only supporting form='FB' " + \
                  'for multi-timing tdac'
            raise ValueError(msg)
        multi_timing = True
    else: # single timing
        tdac = carray(tdac)
        if np.prod(tdac.shape) != 2:
            msg = 'For single-timing tdac, len(tdac) must be 2.'
            raise ValueError(msg)
        tdac.reshape((2,))
        multi_timing = False
    if ordering is None:
        ordering = np.arange(order2)
    if bp is None:
        bp = np.zeros((order2,))
    if not multi_timing:
        # Need direct terms for every interval of memory in the DAC
        n_direct = np.ceil(tdac[1]) - 1
        if tdac[0] > 0 and tdac[0] < 1 and tdac[1] > 1 and tdac[1] < 2:
            n_extra = n_direct - 1 #  tdac pulse spans a sample point
        else:
            n_extra = n_direct
        tdac2 = np.vstack(
                 (np.array((-1, -1)),
                  np.array(tdac).reshape((1, 2)),
                  0.5*np.dot(np.ones((n_extra, 1)), np.array([[-1, 1]]))
                  + np.cumsum(np.ones((n_extra, 2)), 0) + (n_direct - n_extra)
                 ))
    else:
        n_direct = 0
        n_extra = 0
    if ABCDc is None:
        ABCDc = np.zeros((order + 1, order + 2))
        # Stuff the A portion
        if odd:
            ABCDc[0, 0] = np.real(np.log(ntf_z[0]))
            ABCDc[1, 0] = 1
        dline = np.array([0, 1, 2])
        for i in range(order2):
            n = bp[i]
            i1 = 2*i + odd
            zi = 2*ordering[i] + odd
            w = np.abs(np.angle(ntf_z[zi]))
            ABCDc[i1 + dline, i1] = np.array([0, 1, n])
            ABCDc[i1 + dline, i1 + 1] = np.array([-w**2, 0, 1 - n])
        ABCDc[0, order] = 1
        # 2006.10.02 Changed to -1 to make FF STF have +ve gain at DC
        ABCDc[0, order + 1] = -1
    Ac = ABCDc[:order, :order]
    if form == 'FB':
        Cc = ABCDc[order, :order].reshape((1, -1))
        if not multi_timing:
            Bc = np.hstack((np.eye(order), np.zeros((order, 1))))
            Dc = np.hstack((np.zeros((1, order)), np.array([[1]])))
            tp = np.tile(np.array(tdac).reshape((1, 2)), (order + 1, 1))
        else: #Assemble tdac2, Bc and Dc
            tdac2 = np.array([[-1, -1]])
            Bc = None
            Dc = None
            Bci = np.hstack((np.eye(order), np.zeros((order, 1))))
            Dci = np.hstack((np.zeros((1, order)), np.array([[1]])))
            for i in range(len(tdac)):
                tdi = tdac[i]
                if (type(tdi) in (tuple, list)) and len(tdi) and \
                   (type(tdi[0]) in (list, tuple)):
                    for j in range(len(tdi)):
                        tdj = tdi[j]
                        tdac2 = np.vstack((tdac2,
                                           np.array(tdj).reshape(1,-1)))
                        if Bc is not None:
                            Bc = np.hstack((Bc, Bci[:, i].reshape((-1, 1))))
                        else:
                            Bc = Bci[:, i].reshape((-1, 1))
                        if Dc is not None:
                            Dc = np.hstack((Dc, Dci[:, i].reshape((-1, 1))))
                        else:
                            Dc = Dci[:, i].reshape((-1, 1))
                elif len(tdi): # we got tdac[i] = [a, b] where a, b are scalars
                    tdac2 = np.vstack((tdac2,
                                       np.array(tdi).reshape(1,-1)))
                    if Bc is not None:
                        Bc = np.hstack((Bc, Bci[:, i].reshape((-1, 1))))
                    else:
                        Bc = Bci[:, i].reshape((-1, 1))
                    if Dc is not None:
                        Dc = np.hstack((Dc, Dci[:, i].reshape((-1, 1))))
                    else:
                        Dc = Dci[:, i].reshape((-1, 1))
            tp = tdac2[1:, :]
    elif form == 'FF':
        Cc = np.vstack((np.eye(order), np.zeros((1, order))))
        Bc = np.vstack((np.array([[-1]]), np.zeros((order-1, 1))))
        Dc = np.vstack((np.zeros((order, 1)), np.array([[1]])))
        tp = tdac #  2008-03-24 fix from Ayman Shabra
    else:
        raise ValueError('Sorry, no code for form "%s".', form)

    n_imp = np.ceil(2*order + np.max(tdac2[:, 1]) + 1)
    if method == 'LOOP':
        # Sample the L1 impulse response
        y = impL1(ntf, n_imp)
    else:
        # Sample the NTF impulse response
        y = dimpulse((ntf_z, ntf_p, 1., 1.), t=np.arange(n_imp+1))[1][0]
        y = np.atleast_1d(y.squeeze())
    sys_c = []
    for i in range(Bc.shape[1]): # number of inputs
        sys_tmp = []
        for j in range(Cc.shape[0]): # number of outputs
            sys_tmp.append(ss2zpk(Ac, Bc, Cc[j, :], Dc[j, :], input=i))
        sys_c.append(sys_tmp)
    yy = pulse(sys_c, tp, 1, n_imp, 1)
    yy = np.squeeze(yy)
    # Endow yy with n_extra extra impulses.
    # These will need to be implemented with n_extra extra DACs.
    # !! Note: if t1=int, matlab says pulse(sys) @t1 ~=0
    # !! This code corrects this problem.
    if n_extra > 0:
        y_right = padb(np.vstack((np.zeros((1, n_direct)),
                                  np.eye(n_direct))),
                       n_imp + 1)
        # Replace the last column in yy with an ordered set of impulses
        if (n_direct > n_extra):
            yy = np.hstack((yy, y_right[:, 1:]))
        else:
            yy = np.hstack((yy[:, :-1], y_right))

    if method == 'NTF':
        # convolve CT loop response and NTF response
        yynew = None
        for i in range(yy.shape[1]):
            yytmp = np.convolve(yy[:, i], y)[:-n_imp]
            if yynew is None:
                yynew = yytmp.reshape((-1, 1))
            else:
                yynew = np.hstack((yynew, yytmp.reshape((-1, 1))))
        yy = yynew
        e1 = np.zeros(y.shape)
        e1[0] = 1.
        y = y - e1
    # Solve for the coefficients
    x = linalg.lstsq(yy, y)[0]
    if linalg.norm(np.dot(yy, x) - y) > 0.0001:
        warn('Pulse response fit is poor.')
    if form == 'FB':
        if not multi_timing:
            Bc2 = np.hstack((x[:order].reshape((-1, 1)),
                             np.zeros((order, n_extra))))
            if n_extra > 0:
                Dc2 = np.hstack((np.array([[0]]),
                                 x[order:].reshape((-1, 1))))
            else:
                Dc2 = x[order:].reshape((-1, 1))
        else:
            BcDc = np.vstack((Bc, Dc))
            i = np.nonzero(BcDc)
            BcDc[i] = x
            Bc2 = BcDc[:-1, :]
            Dc2 = BcDc[-1, :]
    elif form == 'FF':
        Bc2 = np.hstack((Bc, np.zeros((order, n_extra))))
        Cc = x[:order].reshape((1, -1))
        if n_extra > 0:
            Dc2 = np.hstack((np.array([[0]]), x[order:].T))
        else:
            Dc2 = x[order:].T

    Dc1 = np.zeros((1, 1))
    Dc = np.hstack((Dc1, np.atleast_2d(Dc2)))
    Bc1 = np.vstack((np.ones((1, 1)), np.zeros((order - 1, 1))))
    Bc = np.hstack((Bc1, Bc2))
    # Scale Bc1 for unity STF magnitude at f0
    fz = np.angle(ntf_z)/(2*np.pi)
    f1 = fz[0]
    ibz = np.abs(fz - f1) <= np.abs(fz + f1)
    fz = fz[ibz]
    f0 = np.mean(fz)
    if np.min(np.abs(fz)) < 3*np.min(np.abs(fz - f0)):
        f0 = 0
    L0c = ss2zpk(Ac, Bc1, Cc, Dc1)
    G0 = evalTFP(L0c, ntf, f0)
    if f0 == 0:
        Bc[:, 0] = np.dot(Bc[:, 0],
                          np.abs(np.dot(Bc[0, 1:],
                                        (tdac2[1:, 1] - tdac2[1:, 0]))
                                 /Bc[0, 0]))
    else:
        Bc[:, 0] = Bc[:, 0]/np.abs(G0)
    ABCDc = np.vstack((
                      np.hstack((Ac, Bc)),
                      np.hstack((Cc, Dc))
                     ))
    #ABCDc = np.dot(ABCDc, np.abs(ABCDc) > eps**(1./2.))
    ABCDc[np.nonzero(np.abs(ABCDc) < eps**(1./2))] = 0.
    return ABCDc, tdac2
 def test_impulse_invariant(self, sys, sample_time, samples_number):
     time = np.arange(samples_number) * sample_time
     _, yout_cont = impulse2(sys, T=time, **self.tolerances)
     _, yout_disc = dimpulse(c2d(sys, sample_time, method='impulse'),
                             n=len(time))
     assert_allclose(sample_time * yout_cont.ravel(), yout_disc[0].ravel())
Example #12
0
def l1norm(H):
	"""Compute the l1-norm of a z-domain transfer function.
	"""
	_, y = dimpulse(H, t=np.arange(100))
	return np.sum(np.abs(y[0]))