# Source code for deltasigma._mapABCD

# -*- coding: utf-8 -*-
# _mapABCD.py
# Module providing the mapABCD function
# This file is part of python-deltasigma.
#
# python-deltasigma is a 1:1 Python replacement of Richard Schreier's
# MATLAB delta sigma toolbox (aka "delsigma"), upon which it is heavily based.
# The delta sigma toolbox is (c) 2009, Richard Schreier.
#
# python-deltasigma is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# LICENSE file for the licensing terms.

"""Module providing the mapABCD() function
"""

from __future__ import division, print_function

import numpy as np

from ._utils import diagonal_indices

[docs]def mapABCD(ABCD, form='CRFB'):
"""Compute the coefficients for the specified structure.

It is assumed that the ABCD matrix fits the topology.

**Parameters:**

ABCD : ndarray
A state-space description of the modulator loop filter.
form : str, optional
See :func:realizeNTF for a list of supported structures.

**Returns:**

a : ndarray
Feedback/feedforward coefficients from/to the quantizer. Length :math:n.
g : ndarray
Resonator coefficients. Length :math:floor(n/2).
b : ndarray
Feed-in coefficients from the modulator input to each integrator. Length :math:n + 1.
c : ndarray
Integrator inter-stage coefficients. Length :math:n.

.. seealso::

* :func:realizeNTF for a list of supported structures.

* :func:stuffABCD, the inverse function.

"""
ABCD = np.copy(ABCD)
order = ABCD.shape[0] - 1
odd = order % 2
even = 1 - odd
diagonal = diagonal_indices(ABCD)
subdiag = diagonal_indices(ABCD, -1)
supdiag = [a[odd:order - 1:2] for a in diagonal_indices(ABCD, +1)]
if form in ('CRFB', 'CIFB', 'CRFBD'):
c = ABCD[subdiag]
g = -ABCD[supdiag]
if form == 'CRFB':
dly = np.arange(1 + odd, order, 2)
ABCD[dly, :] = ABCD[dly, :] \
- np.dot(np.diag(c[dly - 1]), ABCD[dly - 1, :])
elif form == 'CRFBD':
dly = np.arange(odd, order, 2)
ABCD[dly, :] = ABCD[dly, :] + np.dot(np.diag(g), ABCD[dly + 1, :])
if order > 2:
coupl = np.arange(1 + even, order, 2)
ABCD[coupl, :] = ABCD[coupl, :] \
- np.dot(np.diag(c[coupl - 1]), ABCD[coupl - 1, :])
a = -ABCD[:order, order + 1].T
b = ABCD[:, order].T
elif form == 'CRFF':
a = np.zeros((order,))
c = np.concatenate((
np.array((-ABCD[0, order + 1],)),
ABCD[subdiag][:-1]
))
g = -ABCD[supdiag]
if even:
multg = np.arange(0, order, 2)
ABCD[multg, :] = ABCD[multg, :] + np.dot(np.diag(g), ABCD[multg + 1, :])
multc = np.arange(2, order, 2)
ABCD[multc, :] = ABCD[multc, :] - np.dot(np.diag(c[multc]), ABCD[multc - 1, :])
a[1:order:2] = ABCD[order, 1:order:2]
for i in range(1, order, 2):
ABCD[order, :] = ABCD[order, :] - a[i]*ABCD[i, :]
a[:order:2] = ABCD[order, :order:2]
b = ABCD[:, order].T
elif form == 'CRFFD':
order = order - 1
odd = order % 2
even = 1 - odd
diagonal = diagonal_indices(ABCD[:order, :order])
subdiag = diagonal_indices(ABCD[:order, :order], -1)
supdiag = [a[odd:order:2] for a in
diagonal_indices(ABCD[:order, :order], +1)]
g = -ABCD[supdiag]
c = np.concatenate((np.atleast_1d(-ABCD[0, order + 2]), ABCD[subdiag]))
a = np.zeros((order, ))
for i in range(0, order, 2):
a[i] = ABCD[order, i]
ABCD[order, :] = ABCD[order, :] - a[i]*ABCD[i, :]
a[1:order:2] = ABCD[order, 1:order:2]
b = ABCD[:order + 1, order + 1].T
for i in range(1, order, 2):
b[i] = b[i] - c[i]*b[i - 1]
if odd:
b[i] = b[i] + g[(i - 1)/2]*b[i + 1]
yscale = ABCD[order + 1, order]
a = a*yscale
b[-1] = b[-1]*yscale
elif form in ('CIFF', 'Stratos'):
a = ABCD[order, :order]
c = np.concatenate((np.atleast_1d(-ABCD[0, order + 1]),
ABCD[subdiag][:-1]))
g = -ABCD[supdiag]
b = ABCD[:, order].T
else:
raise ValueError('Form %s is not yet supported.' % form)

a = np.atleast_1d(a.squeeze())
g = np.atleast_1d(g.squeeze())
b = np.atleast_1d(b.squeeze())
c = np.atleast_1d(c.squeeze())
return a, g, b, c