return self._joints.get(key, None)<EOL>

Get a joint by key.

        Parameters
        ----------
        key : str
            The key for a joint to look up.

        Returns
        -------
        joint : :class:`Joint`
            The joint in the world with the given key, or None if there is no
            such joint.


def create_body(self, shape, name=None, **kwargs):

shape = shape.lower()<EOL>if name is None:<EOL><INDENT>for i in range(<NUM_LIT:1> + len(self._bodies)):<EOL><INDENT>name = '<STR_LIT>'.format(shape, i)<EOL>if name not in self._bodies:<EOL><INDENT>break<EOL><DEDENT><DEDENT><DEDENT>self._bodies[name] = Body.build(shape, name, self, **kwargs)<EOL>return self._bodies[name]<EOL>

Create a new body.

        Parameters
        ----------
        shape : str
            The "shape" of the body to be created. This should name a type of
            body object, e.g., "box" or "cap".
        name : str, optional
            The name to use for this body. If not given, a default name will be
            constructed of the form "{shape}{# of objects in the world}".

        Returns
        -------
        body : :class:`Body`
            The created body object.


def join(self, shape, body_a, body_b=None, name=None, **kwargs):

ba = self.get_body(body_a)<EOL>bb = self.get_body(body_b)<EOL>shape = shape.lower()<EOL>if name is None:<EOL><INDENT>name = '<STR_LIT>'.format(ba.name, shape, bb.name if bb else '<STR_LIT>')<EOL><DEDENT>self._joints[name] = Joint.build(<EOL>shape, name, self, body_a=ba, body_b=bb, **kwargs)<EOL>return self._joints[name]<EOL>

Create a new joint that connects two bodies together.

        Parameters
        ----------
        shape : str
            The "shape" of the joint to use for joining together two bodies.
            This should name a type of joint, such as "ball" or "piston".
        body_a : str or :class:`Body`
            The first body to join together with this joint. If a string is
            given, it will be used as the name of a body to look up in the
            world.
        body_b : str or :class:`Body`, optional
            If given, identifies the second body to join together with
            ``body_a``. If not given, ``body_a`` is joined to the world.
        name : str, optional
            If given, use this name for the created joint. If not given, a name
            will be constructed of the form
            "{body_a.name}^{shape}^{body_b.name}".

        Returns
        -------
        joint : :class:`Joint`
            The joint object that was created.


def move_next_to(self, body_a, body_b, offset_a, offset_b):

ba = self.get_body(body_a)<EOL>bb = self.get_body(body_b)<EOL>if ba is None:<EOL><INDENT>return bb.relative_offset_to_world(offset_b)<EOL><DEDENT>if bb is None:<EOL><INDENT>return ba.relative_offset_to_world(offset_a)<EOL><DEDENT>anchor = ba.relative_offset_to_world(offset_a)<EOL>offset = bb.relative_offset_to_world(offset_b)<EOL>bb.position = bb.position + anchor - offset<EOL>return anchor<EOL>

Move one body to be near another one.

        After moving, the location described by ``offset_a`` on ``body_a`` will
        be coincident with the location described by ``offset_b`` on ``body_b``.

        Parameters
        ----------
        body_a : str or :class:`Body`
            The body to use as a reference for moving the other body. If this is
            a string, it is treated as the name of a body to look up in the
            world.
        body_b : str or :class:`Body`
            The body to move next to ``body_a``. If this is a string, it is
            treated as the name of a body to look up in the world.
        offset_a : 3-tuple of float
            The offset of the anchor point, given as a relative fraction of the
            size of ``body_a``. See :func:`Body.relative_offset_to_world`.
        offset_b : 3-tuple of float
            The offset of the anchor point, given as a relative fraction of the
            size of ``body_b``.

        Returns
        -------
        anchor : 3-tuple of float
            The location of the shared point, which is often useful to use as a
            joint anchor.


return [b.state for b in self.bodies]<EOL>

Return the complete state of all bodies in the world.

        Returns
        -------
        states : list of state information tuples
            A list of body state information for each body in the world. See
            :func:`Body.state`.


for state in states:<EOL><INDENT>self.get_body(state.name).state = state<EOL><DEDENT>

Set the states of some bodies in the world.

        Parameters
        ----------
        states : sequence of states
            A complete state tuple for one or more bodies in the world. See
            :func:`get_body_states`.


self.frame_no += <NUM_LIT:1><EOL>dt = self.dt / substeps<EOL>for _ in range(substeps):<EOL><INDENT>self.ode_contactgroup.empty()<EOL>self.ode_space.collide(None, self.on_collision)<EOL>self.ode_world.step(dt)<EOL><DEDENT>

Step the world forward by one frame.

        Parameters
        ----------
        substeps : int, optional
            Split the step into this many sub-steps. This helps to prevent the
            time delta for an update from being too large.


return False<EOL>

Return True iff the world needs to be reset.


pass<EOL>


def on_key_press(self, key, modifiers, keymap):

if key == keymap.ENTER:<EOL><INDENT>self.reset()<EOL>return True<EOL><DEDENT>

Handle an otherwise unhandled keypress event (from a GUI).


def are_connected(self, body_a, body_b):

return bool(ode.areConnected(<EOL>self.get_body(body_a).ode_body,<EOL>self.get_body(body_b).ode_body))<EOL>

Determine whether the given bodies are currently connected.

        Parameters
        ----------
        body_a : str or :class:`Body`
            One body to test for connectedness. If this is a string, it is
            treated as the name of a body to look up.
        body_b : str or :class:`Body`
            One body to test for connectedness. If this is a string, it is
            treated as the name of a body to look up.

        Returns
        -------
        connected : bool
            Return True iff the two bodies are connected.


def on_collision(self, args, geom_a, geom_b):

body_a = geom_a.getBody()<EOL>body_b = geom_b.getBody()<EOL>if ode.areConnected(body_a, body_b) or(body_a and body_a.isKinematic()) or(body_b and body_b.isKinematic()):<EOL><INDENT>return<EOL><DEDENT>for c in ode.collide(geom_a, geom_b):<EOL><INDENT>c.setBounce(self.elasticity)<EOL>c.setMu(self.friction)<EOL>ode.ContactJoint(self.ode_world, self.ode_contactgroup, c).attach(<EOL>geom_a.getBody(), geom_b.getBody())<EOL><DEDENT>

Callback function for the collide() method.

        Parameters
        ----------
        args : None
            Arguments passed when the callback was registered. Not used.
        geom_a : ODE geometry
            The geometry object of one of the bodies that has collided.
        geom_b : ODE geometry
            The geometry object of one of the bodies that has collided.


def parse(source, world, jointgroup=None, density=<NUM_LIT:1000>, color=None):

visitor = Visitor(world, jointgroup, density, color)<EOL>visitor.parse(re.sub(r'<STR_LIT>', '<STR_LIT:U+0020>', source.read()))<EOL>return visitor<EOL>

Load and parse a source file.

    Parameters
    ----------
    source : file
        A file-like object that contains text information describing bodies and
        joints to add to the world.
    world : :class:`pagoda.physics.World`
        The world to add objects and joints to.
    jointgroup : ode.JointGroup, optional
        If provided, add all joints from this parse to the given group. The
        default behavior adds joints to the world without an explicit group.
    density : float, optional
        Default density for bodies. This is overridden if the source provides a
        density or mass value for a given body.
    color : tuple of floats, optional
        Default color for bodies from this source. Defaults to None, which does
        not assign a color to parsed bodies.


def parse_asf(source, world, jointgroup=None, density=<NUM_LIT:1000>, color=None):

visitor = AsfVisitor(world, jointgroup, density, color)<EOL>visitor.parse(re.sub(r'<STR_LIT>', '<STR_LIT:U+0020>', source.read()))<EOL>return visitor<EOL>


lines = <NUM_LIT:0><EOL>frames = <NUM_LIT:1><EOL>frame = {}<EOL>degrees = False<EOL>for line in source:<EOL><INDENT>lines += <NUM_LIT:1><EOL>line = line.split('<STR_LIT:#>')[<NUM_LIT:0>].strip()<EOL>if not line:<EOL><INDENT>continue<EOL><DEDENT>if line.startswith('<STR_LIT::>'):<EOL><INDENT>if line.lower().startswith('<STR_LIT>'):<EOL><INDENT>degrees = True<EOL><DEDENT>continue<EOL><DEDENT>if line.isdigit():<EOL><INDENT>if int(line) != frames:<EOL><INDENT>raise RuntimeError(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'.format(lines, frames, line))<EOL><DEDENT>yield frame<EOL>frames += <NUM_LIT:1><EOL>frame = {}<EOL>continue<EOL><DEDENT>fields = line.split()<EOL>frame[fields[<NUM_LIT:0>]] = list(map(float, fields[<NUM_LIT:1>:]))<EOL><DEDENT>

Parse an AMC motion capture data file.

    Parameters
    ----------
    source : file
        A file-like object that contains AMC motion capture text.

    Yields
    ------
    frame : dict
        Yields a series of motion capture frames. Each frame is a dictionary
        that maps a bone name to a list of the DOF configurations for that bone.


return <NUM_LIT> / (<NUM_LIT> * self.units['<STR_LIT>'])<EOL>

Return a factor to convert length-scaled inches to mm.


def create_bodies(self, translate=(<NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>), size=<NUM_LIT:0.1>):

stack = [('<STR_LIT:root>', <NUM_LIT:0>, self.root['<STR_LIT>'] + translate)]<EOL>while stack:<EOL><INDENT>name, depth, end = stack.pop()<EOL>for child in self.hierarchy.get(name, ()):<EOL><INDENT>stack.append((child, depth + <NUM_LIT:1>, end + self.bones[child].end))<EOL><DEDENT>if name not in self.bones:<EOL><INDENT>continue<EOL><DEDENT>bone = self.bones[name]<EOL>body = self.world.create_body(<EOL>'<STR_LIT>', name=bone.name, density=self.density,<EOL>lengths=(size, size, bone.length))<EOL>body.color = self.color<EOL>x, y, z = end - bone.direction * bone.length / <NUM_LIT:2><EOL>body.position = x, z, y<EOL>u = bone.direction<EOL>v = np.cross(u, [<NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>])<EOL>l = np.linalg.norm(v)<EOL>if l > <NUM_LIT:0>:<EOL><INDENT>v /= l<EOL>rot = np.vstack([np.cross(u, v), v, u]).T<EOL>swizzle = [[<NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>], [<NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>], [<NUM_LIT:0>, -<NUM_LIT:1>, <NUM_LIT:0>]]<EOL>body.rotation = np.dot(swizzle, rot)<EOL><DEDENT>self.bodies.append(body)<EOL><DEDENT>

Traverse the bone hierarchy and create physics bodies.


stack = ['<STR_LIT:root>']<EOL>while stack:<EOL><INDENT>parent = stack.pop()<EOL>for child in self.hierarchy.get(parent, ()):<EOL><INDENT>stack.append(child)<EOL><DEDENT>if parent not in self.bones:<EOL><INDENT>continue<EOL><DEDENT>bone = self.bones[parent]<EOL>body = [b for b in self.bodies if b.name == parent][<NUM_LIT:0>]<EOL>for child in self.hierarchy.get(parent, ()):<EOL><INDENT>child_bone = self.bones[child]<EOL>child_body = [b for b in self.bodies if b.name == child][<NUM_LIT:0>]<EOL>shape = ('<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>')[len(child_bone.dof)]<EOL>self.joints.append(self.world.join(shape, body, child_body))<EOL><DEDENT><DEDENT>

Traverse the bone hierarchy and create physics joints.


def pid(kp=<NUM_LIT:0.>, ki=<NUM_LIT:0.>, kd=<NUM_LIT:0.>, smooth=<NUM_LIT:0.1>):

state = dict(p=<NUM_LIT:0>, i=<NUM_LIT:0>, d=<NUM_LIT:0>)<EOL>def control(error, dt=<NUM_LIT:1>):<EOL><INDENT>state['<STR_LIT:d>'] = smooth * state['<STR_LIT:d>'] + (<NUM_LIT:1> - smooth) * (error - state['<STR_LIT:p>']) / dt<EOL>state['<STR_LIT:i>'] += error * dt<EOL>state['<STR_LIT:p>'] = error<EOL>return kp * state['<STR_LIT:p>'] + ki * state['<STR_LIT:i>'] + kd * state['<STR_LIT:d>']<EOL><DEDENT>return control<EOL>

r'''Create a callable that implements a PID controller.

    A PID controller returns a control signal :math:`u(t)` given a history of
    error measurements :math:`e(0) \dots e(t)`, using proportional (P), integral
    (I), and derivative (D) terms, according to:

    .. math::

       u(t) = kp * e(t) + ki * \int_{s=0}^t e(s) ds + kd * \frac{de(s)}{ds}(t)

    The proportional term is just the current error, the integral term is the
    sum of all error measurements, and the derivative term is the instantaneous
    derivative of the error measurement.

    Parameters
    ----------
    kp : float
        The weight associated with the proportional term of the PID controller.
    ki : float
        The weight associated with the integral term of the PID controller.
    kd : float
        The weight associated with the derivative term of the PID controller.
    smooth : float in [0, 1]
        Derivative values will be smoothed with this exponential average. A
        value of 1 never incorporates new derivative information, a value of 0.5
        uses the mean of the historic and new information, and a value of 0
        discards historic information (i.e., the derivative in this case will be
        unsmoothed). The default is 0.1.

    Returns
    -------
    controller : callable (float, float) -> float
        Returns a function that accepts an error measurement and a delta-time
        value since the previous measurement, and returns a control signal.


arr = []<EOL>for x in iterables:<EOL><INDENT>arr.extend(x)<EOL><DEDENT>return np.array(arr)<EOL>

Given a sequence of sequences, return a flat numpy array.

    Parameters
    ----------
    iterables : sequence of sequence of number
        A sequence of tuples or lists containing numbers. Typically these come
        from something that represents each joint in a skeleton, like angle.

    Returns
    -------
    ndarray :
        An array of flattened data from each of the source iterables.


if hasattr(source, '<STR_LIT>') and source.lower().endswith('<STR_LIT>'):<EOL><INDENT>self.load_asf(source, **kwargs)<EOL><DEDENT>else:<EOL><INDENT>self.load_skel(source, **kwargs)<EOL><DEDENT>

Load a skeleton definition from a file.

        Parameters
        ----------
        source : str or file
            A filename or file-like object that contains text information
            describing a skeleton. See :class:`pagoda.parser.Parser` for more
            information about the format of the text file.


logging.info('<STR_LIT>', source)<EOL>if hasattr(source, '<STR_LIT>'):<EOL><INDENT>p = parser.parse(source, self.world, self.jointgroup, **kwargs)<EOL><DEDENT>else:<EOL><INDENT>with open(source) as handle:<EOL><INDENT>p = parser.parse(handle, self.world, self.jointgroup, **kwargs)<EOL><DEDENT><DEDENT>self.bodies = p.bodies<EOL>self.joints = p.joints<EOL>self.set_pid_params(kp=<NUM_LIT> / self.world.dt)<EOL>

Load a skeleton definition from a text file.

        Parameters
        ----------
        source : str or file
            A filename or file-like object that contains text information
            describing a skeleton. See :class:`pagoda.parser.BodyParser` for
            more information about the format of the text file.


if hasattr(source, '<STR_LIT>'):<EOL><INDENT>p = parser.parse_asf(source, self.world, self.jointgroup, **kwargs)<EOL><DEDENT>else:<EOL><INDENT>with open(source) as handle:<EOL><INDENT>p = parser.parse_asf(handle, self.world, self.jointgroup, **kwargs)<EOL><DEDENT><DEDENT>self.bodies = p.bodies<EOL>self.joints = p.joints<EOL>self.set_pid_params(kp=<NUM_LIT> / self.world.dt)<EOL>

Load a skeleton definition from an ASF text file.

        Parameters
        ----------
        source : str or file
            A filename or file-like object that contains text information
            describing a skeleton, in ASF format.


def set_pid_params(self, *args, **kwargs):

for joint in self.joints:<EOL><INDENT>joint.target_angles = [None] * joint.ADOF<EOL>joint.controllers = [pid(*args, **kwargs) for i in range(joint.ADOF)]<EOL><DEDENT>

Set PID parameters for all joints in the skeleton.

        Parameters for this method are passed directly to the `pid` constructor.


return sum(j.ADOF for j in self.joints)<EOL>

Return the number of degrees of freedom in the skeleton.


return as_flat_array(j.angles for j in self.joints)<EOL>

Get a list of all current joint angles in the skeleton.


return as_flat_array(j.velocities for j in self.joints)<EOL>

Get a list of all current joint velocities in the skeleton.


return as_flat_array(getattr(j, '<STR_LIT>', j).feedback[-<NUM_LIT:1>][:j.ADOF]<EOL>for j in self.joints)<EOL>

Get a list of all current joint torques in the skeleton.


return as_flat_array(b.position for b in self.bodies)<EOL>

Get a list of all current body positions in the skeleton.


return as_flat_array(b.quaternion for b in self.bodies)<EOL>

Get a list of all current body rotations in the skeleton.


return as_flat_array(b.linear_velocity for b in self.bodies)<EOL>

Get a list of all current body velocities in the skeleton.


return as_flat_array(b.angular_velocity for b in self.bodies)<EOL>

Get a list of all current body angular velocities in the skeleton.


j = <NUM_LIT:0><EOL>for joint in self.joints:<EOL><INDENT>if joint.name == name:<EOL><INDENT>return list(range(j, j + joint.ADOF))<EOL><DEDENT>j += joint.ADOF<EOL><DEDENT>return []<EOL>

Get a list of the indices for a specific joint.

        Parameters
        ----------
        name : str
            The name of the joint to look up.

        Returns
        -------
        list of int :
            A list of the index values for quantities related to the named
            joint. Often useful for getting, say, the angles for a specific
            joint in the skeleton.


def indices_for_body(self, name, step=<NUM_LIT:3>):

for j, body in enumerate(self.bodies):<EOL><INDENT>if body.name == name:<EOL><INDENT>return list(range(j * step, (j + <NUM_LIT:1>) * step))<EOL><DEDENT><DEDENT>return []<EOL>

Get a list of the indices for a specific body.

        Parameters
        ----------
        name : str
            The name of the body to look up.
        step : int, optional
            The number of numbers for each body. Defaults to 3, should be set
            to 4 for body rotation (since quaternions have 4 values).

        Returns
        -------
        list of int :
            A list of the index values for quantities related to the named body.


return [((np.array(j.anchor) - j.anchor2) ** <NUM_LIT:2>).sum() for j in self.joints]<EOL>

Get the current joint separations for the skeleton.

        Returns
        -------
        distances : list of float
            A list expressing the distance between the two joint anchor points,
            for each joint in the skeleton. These quantities describe how
            "exploded" the bodies in the skeleton are; a value of 0 indicates
            that the constraints are perfectly satisfied for that joint.


return [b.state for b in self.bodies]<EOL>

Return a list of the states of all bodies in the skeleton.


for state in states:<EOL><INDENT>self.world.get_body(state.name).state = state<EOL><DEDENT>

Set the states of all bodies in the skeleton.


for joint in self.joints:<EOL><INDENT>joint.velocities = target<EOL><DEDENT>

Set the target velocity for all joints in the skeleton.

        Often the target is set to 0 to cancel out any desired joint rotation.

        Parameters
        ----------
        target : float, optional
            The target velocity for all joints in the skeleton. Defaults to 0.


for joint in self.joints:<EOL><INDENT>amotor = getattr(joint, '<STR_LIT>', joint)<EOL>amotor.max_forces = max_force<EOL>if max_force > <NUM_LIT:0>:<EOL><INDENT>amotor.enable_feedback()<EOL><DEDENT>else:<EOL><INDENT>amotor.disable_feedback()<EOL><DEDENT><DEDENT>

Enable the joint motors in this skeleton.

        This method sets the maximum force that can be applied by each joint to
        attain the desired target velocities. It also enables torque feedback
        for all joint motors.

        Parameters
        ----------
        max_force : float
            The maximum force that each joint is allowed to apply to attain its
            target velocity.


Disable joint motors in this skeleton.

        This method sets to 0 the maximum force that joint motors are allowed to
        apply, in addition to disabling torque feedback.


j = <NUM_LIT:0><EOL>for joint in self.joints:<EOL><INDENT>velocities = [<EOL>ctrl(tgt - cur, self.world.dt) for cur, tgt, ctrl in<EOL>zip(joint.angles, angles[j:j+joint.ADOF], joint.controllers)]<EOL>joint.velocities = velocities<EOL>j += joint.ADOF<EOL><DEDENT>

Move each joint toward a target angle.

        This method uses a PID controller to set a target angular velocity for
        each degree of freedom in the skeleton, based on the difference between
        the current and the target angle for the respective DOF.

        PID parameters are by default set to achieve a tiny bit less than
        complete convergence in one time step, using only the P term (i.e., the
        P coefficient is set to 1 - \delta, while I and D coefficients are set
        to 0). PID parameters can be updated by calling the `set_pid_params`
        method.

        Parameters
        ----------
        angles : list of float
            A list of the target angles for every joint in the skeleton.


j = <NUM_LIT:0><EOL>for joint in self.joints:<EOL><INDENT>joint.add_torques(<EOL>list(torques[j:j+joint.ADOF]) + [<NUM_LIT:0>] * (<NUM_LIT:3> - joint.ADOF))<EOL>j += joint.ADOF<EOL><DEDENT>

Add torques for each degree of freedom in the skeleton.

        Parameters
        ----------
        torques : list of float
            A list of the torques to add to each degree of freedom in the
            skeleton.


for frame in self._frozen:<EOL><INDENT>for body in frame:<EOL><INDENT>self.draw_body(body)<EOL><DEDENT><DEDENT>for body in self.world.bodies:<EOL><INDENT>self.draw_body(body)<EOL><DEDENT>if hasattr(self.world, '<STR_LIT>'):<EOL><INDENT>window.glColor4f(<NUM_LIT>, <NUM_LIT:0.1>, <NUM_LIT:0.1>, <NUM_LIT>)<EOL>window.glLineWidth(<NUM_LIT:3>)<EOL>for j in self.world.markers.joints.values():<EOL><INDENT>window.glBegin(window.GL_LINES)<EOL>window.glVertex3f(*j.getAnchor())<EOL>window.glVertex3f(*j.getAnchor2())<EOL>window.glEnd()<EOL><DEDENT><DEDENT>


return self.data.shape[<NUM_LIT:0>]<EOL>

Return the number of frames of marker data.


return self.data.shape[<NUM_LIT:1>]<EOL>

Return the number of markers in each frame of data.


return sorted(self.channels, key=lambda c: self.channels[c])<EOL>

Return the names of our marker labels in canonical order.


def load_csv(self, filename, start_frame=<NUM_LIT:10>, max_frames=int(<NUM_LIT>)):

import pandas as pd<EOL>compression = None<EOL>if filename.endswith('<STR_LIT>'):<EOL><INDENT>compression = '<STR_LIT>'<EOL><DEDENT>df = pd.read_csv(filename, compression=compression).set_index('<STR_LIT:time>').fillna(-<NUM_LIT:1>)<EOL>assert self.world.dt == pd.Series(df.index).diff().mean()<EOL>markers = []<EOL>for c in df.columns:<EOL><INDENT>m = re.match(r'<STR_LIT>', c)<EOL>if m:<EOL><INDENT>markers.append(m.group(<NUM_LIT:1>))<EOL><DEDENT><DEDENT>self.channels = self._map_labels_to_channels(markers)<EOL>cols = [c for c in df.columns if re.match(r'<STR_LIT>', c)]<EOL>self.data = df[cols].values.reshape((len(df), len(markers), <NUM_LIT:4>))[start_frame:]<EOL>self.data[:, :, [<NUM_LIT:1>, <NUM_LIT:2>]] = self.data[:, :, [<NUM_LIT:2>, <NUM_LIT:1>]]<EOL>logging.info('<STR_LIT>', filename, self.data.shape)<EOL>self.process_data()<EOL>self.create_bodies()<EOL>

Load marker data from a CSV file.

        The file will be imported using Pandas, which must be installed to use
        this method. (``pip install pandas``)

        The first line of the CSV file will be used for header information. The
        "time" column will be used as the index for the data frame. There must
        be columns named 'markerAB-foo-x','markerAB-foo-y','markerAB-foo-z', and
        'markerAB-foo-c' for marker 'foo' to be included in the model.

        Parameters
        ----------
        filename : str
            Name of the CSV file to load.


def load_c3d(self, filename, start_frame=<NUM_LIT:0>, max_frames=int(<NUM_LIT>)):

import c3d<EOL>with open(filename, '<STR_LIT:rb>') as handle:<EOL><INDENT>reader = c3d.Reader(handle)<EOL>logging.info('<STR_LIT>',<EOL><NUM_LIT:1> / self.world.dt, reader.point_rate)<EOL>self.channels = self._map_labels_to_channels([<EOL>s.strip() for s in reader.point_labels])<EOL>data = []<EOL>for i, (_, frame, _) in enumerate(reader.read_frames()):<EOL><INDENT>if i >= start_frame:<EOL><INDENT>data.append(frame[:, [<NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:2>, <NUM_LIT:4>]])<EOL><DEDENT>if len(data) > max_frames:<EOL><INDENT>break<EOL><DEDENT><DEDENT>self.data = np.array(data)<EOL>if reader.get('<STR_LIT>').string_value.strip().lower() == '<STR_LIT>':<EOL><INDENT>logging.info('<STR_LIT>')<EOL>self.data[:, :, :<NUM_LIT:3>] /= <NUM_LIT><EOL><DEDENT><DEDENT>logging.info('<STR_LIT>', filename, self.data.shape)<EOL>self.process_data()<EOL>self.create_bodies()<EOL>

Load marker data from a C3D file.

        The file will be imported using the c3d module, which must be installed
        to use this method. (``pip install c3d``)

        Parameters
        ----------
        filename : str
            Name of the C3D file to load.
        start_frame : int, optional
            Discard the first N frames. Defaults to 0.
        max_frames : int, optional
            Maximum number of frames to load. Defaults to loading all frames.


self.visibility = self.data[:, :, <NUM_LIT:3>]<EOL>self.positions = self.data[:, :, :<NUM_LIT:3>]<EOL>self.velocities = np.zeros_like(self.positions) + <NUM_LIT:1000><EOL>for frame_no in range(<NUM_LIT:1>, len(self.data) - <NUM_LIT:1>):<EOL><INDENT>prev = self.data[frame_no - <NUM_LIT:1>]<EOL>next = self.data[frame_no + <NUM_LIT:1>]<EOL>for c in range(self.num_markers):<EOL><INDENT>if -<NUM_LIT:1> < prev[c, <NUM_LIT:3>] < <NUM_LIT:100> and -<NUM_LIT:1> < next[c, <NUM_LIT:3>] < <NUM_LIT:100>:<EOL><INDENT>self.velocities[frame_no, c] = (<EOL>next[c, :<NUM_LIT:3>] - prev[c, :<NUM_LIT:3>]) / (<NUM_LIT:2> * self.world.dt)<EOL><DEDENT><DEDENT><DEDENT>self.cfms = np.zeros_like(self.visibility) + self.DEFAULT_CFM<EOL>

Process data to produce velocity and dropout information.


self.bodies = {}<EOL>for label in self.channels:<EOL><INDENT>body = self.world.create_body(<EOL>'<STR_LIT>', name='<STR_LIT>'.format(label), radius=<NUM_LIT>)<EOL>body.is_kinematic = True<EOL>body.color = <NUM_LIT>, <NUM_LIT:0.1>, <NUM_LIT:0.1>, <NUM_LIT:0.5><EOL>self.bodies[label] = body<EOL><DEDENT>

Create physics bodies corresponding to each marker in our data.


def load_attachments(self, source, skeleton):

self.targets = {}<EOL>self.offsets = {}<EOL>filename = source<EOL>if isinstance(source, str):<EOL><INDENT>source = open(source)<EOL><DEDENT>else:<EOL><INDENT>filename = '<STR_LIT>'.format(id(source))<EOL><DEDENT>for i, line in enumerate(source):<EOL><INDENT>tokens = line.split('<STR_LIT:#>')[<NUM_LIT:0>].strip().split()<EOL>if not tokens:<EOL><INDENT>continue<EOL><DEDENT>label = tokens.pop(<NUM_LIT:0>)<EOL>if label not in self.channels:<EOL><INDENT>logging.info('<STR_LIT>', filename, i, label)<EOL>continue<EOL><DEDENT>if not tokens:<EOL><INDENT>continue<EOL><DEDENT>name = tokens.pop(<NUM_LIT:0>)<EOL>bodies = [b for b in skeleton.bodies if b.name == name]<EOL>if len(bodies) != <NUM_LIT:1>:<EOL><INDENT>logging.info('<STR_LIT>',<EOL>filename, i, len(bodies), name)<EOL>continue<EOL><DEDENT>b = self.targets[label] = bodies[<NUM_LIT:0>]<EOL>o = self.offsets[label] =np.array(list(map(float, tokens))) * b.dimensions / <NUM_LIT:2><EOL>logging.info('<STR_LIT>', label, b.name, o)<EOL><DEDENT>

Load attachment configuration from the given text source.

        The attachment configuration file has a simple format. After discarding
        Unix-style comments (any part of a line that starts with the pound (#)
        character), each line in the file is then expected to have the following
        format::

            marker-name body-name X Y Z

        The marker name must correspond to an existing "channel" in our marker
        data. The body name must correspond to a rigid body in the skeleton. The
        X, Y, and Z coordinates specify the body-relative offsets where the
        marker should be attached: 0 corresponds to the center of the body along
        the given axis, while -1 and 1 correspond to the minimal (maximal,
        respectively) extent of the body's bounding box along the corresponding
        dimension.

        Parameters
        ----------
        source : str or file-like
            A filename or file-like object that we can use to obtain text
            configuration that describes how markers are attached to skeleton
            bodies.

        skeleton : :class:`pagoda.skeleton.Skeleton`
            The skeleton to attach our marker data to.


self.jointgroup.empty()<EOL>self.joints = {}<EOL>

Detach all marker bodies from their associated skeleton bodies.


assert not self.joints<EOL>for label, j in self.channels.items():<EOL><INDENT>target = self.targets.get(label)<EOL>if target is None:<EOL><INDENT>continue<EOL><DEDENT>if self.visibility[frame_no, j] < <NUM_LIT:0>:<EOL><INDENT>continue<EOL><DEDENT>if np.linalg.norm(self.velocities[frame_no, j]) > <NUM_LIT:10>:<EOL><INDENT>continue<EOL><DEDENT>joint = ode.BallJoint(self.world.ode_world, self.jointgroup)<EOL>joint.attach(self.bodies[label].ode_body, target.ode_body)<EOL>joint.setAnchor1Rel([<NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>])<EOL>joint.setAnchor2Rel(self.offsets[label])<EOL>joint.setParam(ode.ParamCFM, self.cfms[frame_no, j])<EOL>joint.setParam(ode.ParamERP, self.erp)<EOL>joint.name = label<EOL>self.joints[label] = joint<EOL><DEDENT>self._frame_no = frame_no<EOL>

Attach marker bodies to the corresponding skeleton bodies.

        Attachments are only made for markers that are not in a dropout state in
        the given frame.

        Parameters
        ----------
        frame_no : int
            The frame of data we will use for attaching marker bodies.


for label, j in self.channels.items():<EOL><INDENT>body = self.bodies[label]<EOL>body.position = self.positions[frame_no, j]<EOL>body.linear_velocity = self.velocities[frame_no, j]<EOL><DEDENT>

Reposition markers to a specific frame of data.

        Parameters
        ----------
        frame_no : int
            The frame of data where we should reposition marker bodies. Markers
            will be positioned in the appropriate places in world coordinates.
            In addition, linear velocities of the markers will be set according
            to the data as long as there are no dropouts in neighboring frames.


distances = []<EOL>for label in self.labels:<EOL><INDENT>joint = self.joints.get(label)<EOL>distances.append([np.nan, np.nan, np.nan] if joint is None else<EOL>np.array(joint.getAnchor()) - joint.getAnchor2())<EOL><DEDENT>return np.array(distances)<EOL>

Get a list of the distances between markers and their attachments.

        Returns
        -------
        distances : ndarray of shape (num-markers, 3)
            Array of distances for each marker joint in our attachment setup. If
            a marker does not currently have an associated joint (e.g. because
            it is not currently visible) this will contain NaN for that row.


cfm = self.cfms[self._frame_no][:, None]<EOL>kp = self.erp / (cfm * self.world.dt)<EOL>kd = (<NUM_LIT:1> - self.erp) / cfm<EOL>dx = self.distances()<EOL>F = kp * dx<EOL>if dx_tm1 is not None:<EOL><INDENT>bad = np.isnan(dx) | np.isnan(dx_tm1)<EOL>F[~bad] += (kd * (dx - dx_tm1) / self.world.dt)[~bad]<EOL><DEDENT>return F<EOL>

Return an array of the forces exerted by marker springs.

        Notes
        -----

        The forces exerted by the marker springs can be approximated by::

          F = kp * dx

        where ``dx`` is the current array of marker distances. An even more
        accurate value is computed by approximating the velocity of the spring
        displacement::

          F = kp * dx + kd * (dx - dx_tm1) / dt

        where ``dx_tm1`` is an array of distances from the previous time step.

        Parameters
        ----------
        dx_tm1 : ndarray
            An array of distances from markers to their attachment targets,
            measured at the previous time step.

        Returns
        -------
        F : ndarray
            An array of forces that the markers are exerting on the skeleton.


def load_skeleton(self, filename, pid_params=None):

self.skeleton = skeleton.Skeleton(self)<EOL>self.skeleton.load(filename, color=(<NUM_LIT>, <NUM_LIT:0.5>, <NUM_LIT>, <NUM_LIT>))<EOL>if pid_params:<EOL><INDENT>self.skeleton.set_pid_params(**pid_params)<EOL><DEDENT>self.skeleton.erp = <NUM_LIT:0.1><EOL>self.skeleton.cfm = <NUM_LIT:0><EOL>

Create and configure a skeleton in our model.

        Parameters
        ----------
        filename : str
            The name of a file containing skeleton configuration data.
        pid_params : dict, optional
            If given, use this dictionary to set the PID controller
            parameters on each joint in the skeleton. See
            :func:`pagoda.skeleton.pid` for more information.


def load_markers(self, filename, attachments, max_frames=<NUM_LIT>):

self.markers = Markers(self)<EOL>fn = filename.lower()<EOL>if fn.endswith('<STR_LIT>'):<EOL><INDENT>self.markers.load_c3d(filename, max_frames=max_frames)<EOL><DEDENT>elif fn.endswith('<STR_LIT>') or fn.endswith('<STR_LIT>'):<EOL><INDENT>self.markers.load_csv(filename, max_frames=max_frames)<EOL><DEDENT>else:<EOL><INDENT>logging.fatal('<STR_LIT>', filename)<EOL><DEDENT>self.markers.load_attachments(attachments, self.skeleton)<EOL>

Load marker data and attachment preferences into the model.

        Parameters
        ----------
        filename : str
            The name of a file containing marker data. This currently needs to
            be either a .C3D or a .CSV file. CSV files must adhere to a fairly
            strict column naming convention; see :func:`Markers.load_csv` for
            more information.
        attachments : str
            The name of a text file specifying how markers are attached to
            skeleton bodies.
        max_frames : number, optional
            Only read in this many frames of marker data. By default, the entire
            data file is read into memory.

        Returns
        -------
        markers : :class:`Markers`
            Returns a markers object containing loaded marker data as well as
            skeleton attachment configuration.


<EOL>self.frame_no += <NUM_LIT:1><EOL>try:<EOL><INDENT>next(self.follower)<EOL><DEDENT>except (AttributeError, StopIteration) as err:<EOL><INDENT>self.reset()<EOL><DEDENT>

Advance the physics world by one step.

        Typically this is called as part of a :class:`pagoda.viewer.Viewer`, but
        it can also be called manually (or some other stepping mechanism
        entirely can be used).


self.follower = self.follow_markers()<EOL>

Reset the automatic process that gets called by :func:`step`.

        By default this follows whatever marker data is loaded into our model.

        Provide an override for this method to customize the default behavior of
        the :func:`step` method.


def settle_to_markers(self, frame_no=<NUM_LIT:0>, max_distance=<NUM_LIT>, max_iters=<NUM_LIT>,<EOL>states=None):

if states is not None:<EOL><INDENT>self.skeleton.set_body_states(states)<EOL><DEDENT>dist = None<EOL>for _ in range(max_iters):<EOL><INDENT>for _ in self._step_to_marker_frame(frame_no):<EOL><INDENT>pass<EOL><DEDENT>dist = np.nanmean(abs(self.markers.distances()))<EOL>logging.info('<STR_LIT>', frame_no, dist)<EOL>if dist < max_distance:<EOL><INDENT>return self.skeleton.get_body_states()<EOL><DEDENT>for b in self.skeleton.bodies:<EOL><INDENT>b.linear_velocity = <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0><EOL>b.angular_velocity = <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0><EOL><DEDENT><DEDENT>return states<EOL>

Settle the skeleton to our marker data at a specific frame.

        Parameters
        ----------
        frame_no : int, optional
            Settle the skeleton to marker data at this frame. Defaults to 0.
        max_distance : float, optional
            The settling process will stop when the mean marker distance falls
            below this threshold. Defaults to 0.1m (10cm). Setting this too
            small prevents the settling process from finishing (it will loop
            indefinitely), and setting it too large prevents the skeleton from
            settling to a stable state near the markers.
        max_iters : int, optional
            Attempt to settle markers for at most this many iterations. Defaults
            to 1000.
        states : list of body states, optional
            If given, set the bodies in our skeleton to these kinematic states
            before starting the settling process.


def follow_markers(self, start=<NUM_LIT:0>, end=<NUM_LIT>, states=None):

if states is not None:<EOL><INDENT>self.skeleton.set_body_states(states)<EOL><DEDENT>for frame_no, frame in enumerate(self.markers):<EOL><INDENT>if frame_no < start:<EOL><INDENT>continue<EOL><DEDENT>if frame_no >= end:<EOL><INDENT>break<EOL><DEDENT>for states in self._step_to_marker_frame(frame_no):<EOL><INDENT>yield states<EOL><DEDENT><DEDENT>

Iterate over a set of marker data, dragging its skeleton along.

        Parameters
        ----------
        start : int, optional
            Start following marker data after this frame. Defaults to 0.
        end : int, optional
            Stop following marker data after this frame. Defaults to the end of
            the marker data.
        states : list of body states, optional
            If given, set the states of the skeleton bodies to these values
            before starting to follow the marker data.


def _step_to_marker_frame(self, frame_no, dt=None):

<EOL>self.markers.detach()<EOL>self.markers.reposition(frame_no)<EOL>self.markers.attach(frame_no)<EOL>self.ode_space.collide(None, self.on_collision)<EOL>states = self.skeleton.get_body_states()<EOL>self.skeleton.set_body_states(states)<EOL>yield states<EOL>self.ode_world.step(dt or self.dt)<EOL>self.ode_contactgroup.empty()<EOL>

Update the simulator to a specific frame of marker data.

        This method returns a generator of body states for the skeleton! This
        generator must be exhausted (e.g., by consuming this call in a for loop)
        for the simulator to work properly.

        This process involves the following steps:

        - Move the markers to their new location:
          - Detach from the skeleton
          - Update marker locations
          - Reattach to the skeleton
        - Detect ODE collisions
        - Yield the states of the bodies in the skeleton
        - Advance the ODE world one step

        Parameters
        ----------
        frame_no : int
            Step to this frame of marker data.
        dt : float, optional
            Step with this time duration. Defaults to ``self.dt``.

        Returns
        -------
        states : sequence of state tuples
            A generator of a sequence of one body state for the skeleton. This
            generator must be exhausted for the simulation to work properly.


def inverse_kinematics(self, start=<NUM_LIT:0>, end=<NUM_LIT>, states=None, max_force=<NUM_LIT:20>):

zeros = None<EOL>if max_force > <NUM_LIT:0>:<EOL><INDENT>self.skeleton.enable_motors(max_force)<EOL>zeros = np.zeros(self.skeleton.num_dofs)<EOL><DEDENT>for _ in self.follow_markers(start, end, states):<EOL><INDENT>if zeros is not None:<EOL><INDENT>self.skeleton.set_target_angles(zeros)<EOL><DEDENT>yield self.skeleton.joint_angles<EOL><DEDENT>

Follow a set of marker data, yielding kinematic joint angles.

        Parameters
        ----------
        start : int, optional
            Start following marker data after this frame. Defaults to 0.
        end : int, optional
            Stop following marker data after this frame. Defaults to the end of
            the marker data.
        states : list of body states, optional
            If given, set the states of the skeleton bodies to these values
            before starting to follow the marker data.
        max_force : float, optional
            Allow each degree of freedom in the skeleton to exert at most this
            force when attempting to maintain its equilibrium position. This
            defaults to 20N. Set this value higher to simulate a stiff skeleton
            while following marker data.

        Returns
        -------
        angles : sequence of angle frames
            Returns a generator of joint angle data for the skeleton. One set of
            joint angles will be generated for each frame of marker data between
            `start` and `end`.


def inverse_dynamics(self, angles, start=<NUM_LIT:0>, end=<NUM_LIT>, states=None, max_force=<NUM_LIT:100>):

if states is not None:<EOL><INDENT>self.skeleton.set_body_states(states)<EOL><DEDENT>for frame_no, frame in enumerate(angles):<EOL><INDENT>if frame_no < start:<EOL><INDENT>continue<EOL><DEDENT>if frame_no >= end:<EOL><INDENT>break<EOL><DEDENT>self.ode_space.collide(None, self.on_collision)<EOL>states = self.skeleton.get_body_states()<EOL>self.skeleton.set_body_states(states)<EOL>self.skeleton.enable_motors(max_force)<EOL>self.skeleton.set_target_angles(angles[frame_no])<EOL>self.ode_world.step(self.dt)<EOL>torques = self.skeleton.joint_torques<EOL>self.skeleton.disable_motors()<EOL>self.skeleton.set_body_states(states)<EOL>self.skeleton.add_torques(torques)<EOL>yield torques<EOL>self.ode_world.step(self.dt)<EOL>self.ode_contactgroup.empty()<EOL><DEDENT>

Follow a set of angle data, yielding dynamic joint torques.

        Parameters
        ----------
        angles : ndarray (num-frames x num-dofs)
            Follow angle data provided by this array of angle values.
        start : int, optional
            Start following angle data after this frame. Defaults to the start
            of the angle data.
        end : int, optional
            Stop following angle data after this frame. Defaults to the end of
            the angle data.
        states : list of body states, optional
            If given, set the states of the skeleton bodies to these values
            before starting to follow the marker data.
        max_force : float, optional
            Allow each degree of freedom in the skeleton to exert at most this
            force when attempting to follow the given joint angles. Defaults to
            100N. Setting this value to be large results in more accurate
            following but can cause oscillations in the PID controllers,
            resulting in noisy torques.

        Returns
        -------
        torques : sequence of torque frames
            Returns a generator of joint torque data for the skeleton. One set
            of joint torques will be generated for each frame of angle data
            between `start` and `end`.


def forward_dynamics(self, torques, start=<NUM_LIT:0>, states=None):

if states is not None:<EOL><INDENT>self.skeleton.set_body_states(states)<EOL><DEDENT>for frame_no, torque in enumerate(torques):<EOL><INDENT>if frame_no < start:<EOL><INDENT>continue<EOL><DEDENT>if frame_no >= end:<EOL><INDENT>break<EOL><DEDENT>self.ode_space.collide(None, self.on_collision)<EOL>self.skeleton.add_torques(torque)<EOL>self.ode_world.step(self.dt)<EOL>yield<EOL>self.ode_contactgroup.empty()<EOL><DEDENT>

Move the body according to a set of torque data.


X=np.real(x)        <EOL>Y=np.imag(x)        <EOL>b=np.array([<NUM_LIT:1>, -<NUM_LIT:1>]) <EOL>a=np.array([<NUM_LIT:1>, <NUM_LIT:0>])  <EOL>derY=signal.lfilter(b,a,Y)  <EOL>derX=signal.lfilter(b,a,X)  <EOL>disdata=(X*derY-Y*derX)/(X**<NUM_LIT:2>+Y**<NUM_LIT:2>)<EOL>return disdata<EOL>

function disdata = discrim(x)
where x is an angle modulated signal in complex baseband form.

Mark Wickert


T = <NUM_LIT:1>/float(fs)<EOL>Kv = <NUM_LIT:1.0><EOL>Norder = <NUM_LIT:5><EOL>b_lp,a_lp = signal.butter(Norder,<NUM_LIT:2>*(fq/<NUM_LIT>)/float(fs))<EOL>fstate = np.zeros(Norder) <EOL>Kv = <NUM_LIT:2>*np.pi*Kv <EOL>if loop_type == <NUM_LIT:1>:<EOL><INDENT>fn = Bn<EOL>Kt = <NUM_LIT:2>*np.pi*fn <EOL><DEDENT>elif loop_type == <NUM_LIT:2>:<EOL><INDENT>fn = <NUM_LIT:1>/(<NUM_LIT:2>*np.pi)*<NUM_LIT:2>*Bn/(zeta + <NUM_LIT:1>/(<NUM_LIT:4>*zeta)) <EOL>Kt = <NUM_LIT:4>*np.pi*zeta*fn <EOL>a = np.pi*fn/zeta<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>filt_in_last = <NUM_LIT:0><EOL>filt_out_last = <NUM_LIT:0><EOL>vco_in_last = <NUM_LIT:0><EOL>vco_out = <NUM_LIT:0><EOL>vco_out_last = <NUM_LIT:0><EOL>n = np.arange(<NUM_LIT:0>,len(xr))<EOL>theta = np.zeros(len(xr))<EOL>ev = np.zeros(len(xr))<EOL>phi_error = np.zeros(len(xr))<EOL>for kk in range(len(n)):<EOL><INDENT>phi_error[kk] = <NUM_LIT:2>*xr[kk]*np.sin(vco_out)<EOL>phi_error[kk],fstate = signal.lfilter(b_lp,a_lp,np.array([phi_error[kk]]),zi=fstate)<EOL>pd_out = phi_error[kk]<EOL>gain_out = Kt/Kv*pd_out <EOL>if loop_type == <NUM_LIT:2>:<EOL><INDENT>filt_in = a*gain_out<EOL>filt_out = filt_out_last + T/<NUM_LIT>*(filt_in + filt_in_last)<EOL>filt_in_last = filt_in<EOL>filt_out_last = filt_out<EOL>filt_out = filt_out + gain_out<EOL><DEDENT>else:<EOL><INDENT>filt_out = gain_out<EOL><DEDENT>vco_in = filt_out + fq/(Kv/(<NUM_LIT:2>*np.pi)) <EOL>vco_out = vco_out_last + T/<NUM_LIT>*(vco_in + vco_in_last)<EOL>vco_in_last = vco_in<EOL>vco_out_last = vco_out<EOL>vco_out = Kv*vco_out <EOL>ev[kk] = filt_out<EOL>theta[kk] = np.mod(vco_out,<NUM_LIT:2>*np.pi); <EOL><DEDENT>return theta,phi_error<EOL>

theta, phi_error = pilot_PLL(xr,fq,fs,loop_type,Bn,zeta)

Mark Wickert, April 2014


<EOL>rx_symb_d = np.zeros(int(np.fix(len(y)/Ns)))<EOL>track = np.zeros(int(np.fix(len(y)/Ns)))<EOL>bit_count = -<NUM_LIT:1><EOL>y_abs = np.zeros(len(y))<EOL>clk = np.zeros(len(y))<EOL>k = Ns+<NUM_LIT:1> <EOL>for i in range(len(y)):<EOL><INDENT>if i >= Ns: <EOL><INDENT>y_abs[i] = np.abs(np.sum(y[i-Ns+<NUM_LIT:1>:i+<NUM_LIT:1>]))<EOL>if (k == <NUM_LIT:0>):<EOL><INDENT>w_hat = y_abs[i-<NUM_LIT:2>:i+<NUM_LIT:1>]<EOL>bit_count += <NUM_LIT:1><EOL>if w_hat[<NUM_LIT:1>] != <NUM_LIT:0>:<EOL><INDENT>if w_hat[<NUM_LIT:0>] < w_hat[<NUM_LIT:2>]:<EOL><INDENT>k = Ns-<NUM_LIT:1><EOL>clk[i-<NUM_LIT:2>] = <NUM_LIT:1><EOL>rx_symb_d[bit_count] = y[i-<NUM_LIT:2>-int(np.round(Ns/<NUM_LIT:2>))-<NUM_LIT:1>]<EOL><DEDENT>elif w_hat[<NUM_LIT:0>] > w_hat[<NUM_LIT:2>]:<EOL><INDENT>k = Ns+<NUM_LIT:1><EOL>clk[i] = <NUM_LIT:1><EOL>rx_symb_d[bit_count] = y[i-int(np.round(Ns/<NUM_LIT:2>))-<NUM_LIT:1>]<EOL><DEDENT>else:<EOL><INDENT>k = Ns<EOL>clk[i-<NUM_LIT:1>] = <NUM_LIT:1><EOL>rx_symb_d[bit_count] = y[i-<NUM_LIT:1>-int(np.round(Ns/<NUM_LIT:2>))-<NUM_LIT:1>]<EOL><DEDENT><DEDENT>else:<EOL><INDENT>k = Ns<EOL>clk[i-<NUM_LIT:1>] = <NUM_LIT:1><EOL>rx_symb_d[bit_count] = y[i-<NUM_LIT:1>-int(np.round(Ns/<NUM_LIT:2>))]<EOL><DEDENT>track[bit_count] = np.mod(i,Ns)<EOL><DEDENT><DEDENT>k -= <NUM_LIT:1><EOL><DEDENT>rx_symb_d = rx_symb_d[:bit_count]<EOL>return rx_symb_d, clk, track<EOL>

rx_symb_d,clk,track = sccs_bit_sync(y,Ns)

//////////////////////////////////////////////////////
 Symbol synchronization algorithm using SCCS
//////////////////////////////////////////////////////
     y = baseband NRZ data waveform
    Ns = nominal number of samples per symbol

Reworked from ECE 5675 Project
Translated from m-code version
Mark Wickert April 2014


Nbits = len(rx_data)<EOL>c = dc.m_seq(m)<EOL>if flip == <NUM_LIT:1>:<EOL><INDENT>c.shape = (<NUM_LIT:1>,len(c))<EOL>c = np.fliplr(c).flatten()<EOL><DEDENT>L = int(np.ceil(Nbits/float(len(c))))<EOL>tx_data = np.dot(c.reshape(len(c),<NUM_LIT:1>),np.ones((<NUM_LIT:1>,L)))<EOL>tx_data = tx_data.T.reshape((<NUM_LIT:1>,len(c)*L)).flatten()<EOL>tx_data = tx_data[:Nbits]<EOL>tx_data = <NUM_LIT:2>*tx_data - <NUM_LIT:1><EOL>Bit_count,Bit_errors = dc.BPSK_BEP(rx_data,tx_data)<EOL>print('<STR_LIT>' % (len(rx_data),len(tx_data)))<EOL>Pe = Bit_errors/float(Bit_count)<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>' % Bit_errors)<EOL>print('<STR_LIT>' % Bit_count)<EOL>print('<STR_LIT>' % Pe)<EOL>print('<STR_LIT>')<EOL>

fsk_BEP(rx_data,m,flip)

Estimate the BEP of the data bits recovered
by the RTL-SDR Based FSK Receiver.

The reference m-sequence generated in Python
was found to produce sequences running in the opposite
direction relative to the m-sequences generated by the
mbed. To allow error detection the reference m-sequence
is flipped.

Mark Wickert April 2014


x_wav = np.hstack((np.array([x.real]).T,np.array([x.imag]).T))<EOL>ss.to_wav(filename, rate, x_wav)<EOL>print('<STR_LIT>')<EOL>

Save a complex signal vector to a wav file for compact binary
storage of 16-bit signal samples. The wav left and right channels
are used to save real (I) and imaginary (Q) values. The rate is
just a convent way of documenting the original signal sample rate.

complex2wav(filename,rate,x)

Mark Wickert April 2014


fs, x_LR_cols = ss.from_wav(filename)<EOL>x = x_LR_cols[:,<NUM_LIT:0>] + <NUM_LIT>*x_LR_cols[:,<NUM_LIT:1>]<EOL>return fs,x<EOL>

Return a complex signal vector from a wav file that was used to store
the real (I) and imaginary (Q) values of a complex signal ndarray.
The rate is included as means of recalling the original signal sample
rate.

fs,x = wav2complex(filename)

Mark Wickert April 2014


lnps = len(ps) <EOL>ber = np.zeros(lnps) <EOL>for k in range(<NUM_LIT:0>,lnps): <EOL><INDENT>ser = ps[k] <EOL>sum1 = <NUM_LIT:0> <EOL>sum2 = <NUM_LIT:0><EOL>for i in range(t+<NUM_LIT:1>,d+<NUM_LIT:1>):<EOL><INDENT>term = special.comb(n,i)*(ser**i)*((<NUM_LIT:1>-ser))**(n-i)<EOL>sum1 = sum1 + term<EOL><DEDENT>for i in range(d+<NUM_LIT:1>,n+<NUM_LIT:1>):<EOL><INDENT>term = (i)*special.comb(n,i)*(ser**i)*((<NUM_LIT:1>-ser)**(n-i))<EOL>sum2 = sum2+term<EOL><DEDENT>ber[k] = (q/(<NUM_LIT:2>*(q-<NUM_LIT:1>)))*((d/n)*sum1+(<NUM_LIT:1>/n)*sum2)<EOL><DEDENT>return ber<EOL>

Converts symbol error rate to bit error rate. Taken from Ziemer and
Tranter page 650. Necessary when comparing different types of block codes.

parameters
----------
q: size of the code alphabet for given modulation type (BPSK=2)
n: number of channel bits
d: distance (2e+1) where e is the number of correctable errors per code word.
   For hamming codes, e=1, so d=3.
t: number of correctable errors per code word
ps: symbol error probability vector

returns
-------
ber: bit error rate


Pb = np.zeros_like(SNRdB)<EOL>Ps = np.zeros_like(SNRdB)<EOL>SNR = <NUM_LIT>**(SNRdB/<NUM_LIT>)<EOL>n = <NUM_LIT:2>**j-<NUM_LIT:1><EOL>k = n-j<EOL>for i,SNRn in enumerate(SNR):<EOL><INDENT>if coded: <EOL><INDENT>if M == <NUM_LIT:2>:<EOL><INDENT>Ps[i] = Q_fctn(np.sqrt(k*<NUM_LIT>*SNRn/n))<EOL><DEDENT>else:<EOL><INDENT>Ps[i] = <NUM_LIT>/np.log2(M)*(<NUM_LIT:1> - <NUM_LIT:1>/np.sqrt(M))*np.gaussQ(np.sqrt(<NUM_LIT:3>*np.log2(M)/(M-<NUM_LIT:1>)*SNRn))/k<EOL><DEDENT><DEDENT>else: <EOL><INDENT>if M == <NUM_LIT:2>:<EOL><INDENT>Pb[i] = Q_fctn(np.sqrt(<NUM_LIT>*SNRn))<EOL><DEDENT>else:<EOL><INDENT>Pb[i] = <NUM_LIT>/np.log2(M)*(<NUM_LIT:1> - <NUM_LIT:1>/np.sqrt(M))*np.gaussQ(np.sqrt(<NUM_LIT:3>*np.log2(M)/(M-<NUM_LIT:1>)*SNRn))<EOL><DEDENT><DEDENT><DEDENT>if coded:<EOL><INDENT>Pb = ser2ber(M,n,<NUM_LIT:3>,<NUM_LIT:1>,Ps)<EOL><DEDENT>return Pb<EOL>

Finds the bit error probability bounds according to Ziemer and Tranter
page 656.

parameters:
-----------
j: number of parity bits used in single error correction block code
SNRdB: Eb/N0 values in dB
coded: Select single error correction code (True) or uncoded (False)
M: modulation order

returns:
--------
Pb: bit error probability bound


if(j < <NUM_LIT:3>):<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>n = <NUM_LIT:2>**j-<NUM_LIT:1><EOL>k = n-j<EOL>G = np.zeros((k,n),dtype=int)<EOL>H = np.zeros((j,n),dtype=int)<EOL>P = np.zeros((j,k),dtype=int)<EOL>R = np.zeros((k,n),dtype=int)<EOL>for i in range(<NUM_LIT:1>,n+<NUM_LIT:1>):<EOL><INDENT>b = list(binary(i,j))<EOL>for m in range(<NUM_LIT:0>,len(b)):<EOL><INDENT>b[m] = int(b[m])<EOL><DEDENT>H[:,i-<NUM_LIT:1>] = np.array(b)<EOL><DEDENT>H1 = np.zeros((<NUM_LIT:1>,j),dtype=int)<EOL>H2 = np.zeros((<NUM_LIT:1>,j),dtype=int)<EOL>for i in range(<NUM_LIT:0>,j):<EOL><INDENT>idx1 = <NUM_LIT:2>**i-<NUM_LIT:1><EOL>idx2 = n-i-<NUM_LIT:1><EOL>H1[<NUM_LIT:0>,:] = H[:,idx1]<EOL>H2[<NUM_LIT:0>,:] = H[:,idx2]<EOL>H[:,idx1] = H2<EOL>H[:,idx2] = H1<EOL><DEDENT>P = H[:,:k]<EOL>G[:,:k] = np.diag(np.ones(k))<EOL>G[:,k:] = P.T<EOL>R[:,:k] = np.diag(np.ones(k))<EOL>return G, H, R, n, k<EOL>

Generates parity check matrix (H) and generator
matrix (G).

Parameters
----------
j: Number of Hamming code parity bits with n = 2^j-1 and k = n-j

returns
-------
G: Systematic generator matrix with left-side identity matrix
H: Systematic parity-check matrix with right-side identity matrix
R: k x k identity matrix
n: number of total bits/block
k: number of source bits/block

Andrew Smit November 2018


if(np.dtype(x[<NUM_LIT:0>]) != int):<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>if(len(x) % self.k or len(x) < self.k):<EOL><INDENT>raise ValueError('<STR_LIT>' %self.k)<EOL><DEDENT>N_symbols = int(len(x)/self.k)<EOL>codewords = np.zeros(N_symbols*self.n)<EOL>x = np.reshape(x,(<NUM_LIT:1>,len(x)))<EOL>for i in range(<NUM_LIT:0>,N_symbols):<EOL><INDENT>codewords[i*self.n:(i+<NUM_LIT:1>)*self.n] = np.matmul(x[:,i*self.k:(i+<NUM_LIT:1>)*self.k],self.G)%<NUM_LIT:2><EOL><DEDENT>return codewords<EOL>

Encodes input bit array x using hamming block code.

parameters
----------
x: array of source bits to be encoded by block encoder.

returns
-------
codewords: array of code words generated by generator
matrix G and input x.

Andrew Smit November 2018


if(np.dtype(codewords[<NUM_LIT:0>]) != int):<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>if(len(codewords) % self.n or len(codewords) < self.n):<EOL><INDENT>raise ValueError('<STR_LIT>' %self.n)<EOL><DEDENT>N_symbols = int(len(codewords)/self.n)<EOL>decoded_bits = np.zeros(N_symbols*self.k)<EOL>codewords = np.reshape(codewords,(<NUM_LIT:1>,len(codewords)))<EOL>for i in range(<NUM_LIT:0>,N_symbols):<EOL><INDENT>S = np.matmul(self.H,codewords[:,i*self.n:(i+<NUM_LIT:1>)*self.n].T) % <NUM_LIT:2><EOL>bits = '<STR_LIT>'<EOL>for m in range(<NUM_LIT:0>,len(S)):<EOL><INDENT>bit = str(int(S[m,:]))<EOL>bits = bits + bit<EOL><DEDENT>error_pos = int(bits,<NUM_LIT:2>)<EOL>h_pos = self.H[:,error_pos-<NUM_LIT:1>]<EOL>bits = '<STR_LIT>'<EOL>for m in range(<NUM_LIT:0>,len(S)):<EOL><INDENT>bit = str(int(h_pos[m]))<EOL>bits = bits + bit<EOL><DEDENT>decoded_pos = int(bits,<NUM_LIT:2>)-<NUM_LIT:1><EOL>if(error_pos):<EOL><INDENT>codewords[:,i*self.n+decoded_pos] = (codewords[:,i*self.n+decoded_pos] + <NUM_LIT:1>) % <NUM_LIT:2><EOL><DEDENT>decoded_bits[i*self.k:(i+<NUM_LIT:1>)*self.k] = np.matmul(self.R,codewords[:,i*self.n:(i+<NUM_LIT:1>)*self.n].T).T % <NUM_LIT:2><EOL><DEDENT>return decoded_bits.astype(int)<EOL>

Decode hamming encoded codewords. Make sure code words are of
the appropriate length for the object.

parameters
---------
codewords: bit array of codewords

returns
-------
decoded_bits: bit array of decoded source bits

Andrew Smit November 2018


<EOL>if(len(x) % self.k or len(x) < self.k):<EOL><INDENT>raise ValueError('<STR_LIT>' %self.k)<EOL><DEDENT>if(np.dtype(x[<NUM_LIT:0>]) != int):<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>Num_blocks = int(len(x) / self.k)<EOL>codewords = np.zeros((Num_blocks,self.n),dtype=int)<EOL>x = np.reshape(x,(Num_blocks,self.k))<EOL>for p in range(Num_blocks):<EOL><INDENT>S = np.zeros(len(self.G))<EOL>codeword = np.zeros(self.n)<EOL>current_block = x[p,:]<EOL>for i in range(<NUM_LIT:0>,self.n):<EOL><INDENT>if(i < self.k):<EOL><INDENT>S[<NUM_LIT:0>] = current_block[i]<EOL>S0temp = <NUM_LIT:0><EOL>for m in range(<NUM_LIT:0>,len(self.G)):<EOL><INDENT>if(self.G[m] == '<STR_LIT:1>'):<EOL><INDENT>S0temp = S0temp + S[m]<EOL><DEDENT><DEDENT>S0temp = S0temp % <NUM_LIT:2><EOL>S = np.roll(S,<NUM_LIT:1>)<EOL>codeword[i] = current_block[i]<EOL>S[<NUM_LIT:1>] = S0temp<EOL><DEDENT>else:<EOL><INDENT>out = <NUM_LIT:0><EOL>for m in range(<NUM_LIT:1>,len(self.G)):<EOL><INDENT>if(self.G[m] == '<STR_LIT:1>'):<EOL><INDENT>out = out + S[m]<EOL><DEDENT><DEDENT>codeword[i] = out % <NUM_LIT:2><EOL>S = np.roll(S,<NUM_LIT:1>)<EOL>S[<NUM_LIT:1>] = <NUM_LIT:0><EOL><DEDENT><DEDENT>codewords[p,:] = codeword<EOL><DEDENT>codewords = np.reshape(codewords,np.size(codewords))<EOL>return codewords.astype(int)<EOL>

Encodes input bit array x using cyclic block code.

parameters
----------
x: vector of source bits to be encoded by block encoder. Numpy array
   of integers expected.

returns
-------
codewords: vector of code words generated from input vector

Andrew Smit November 2018


<EOL>if(len(codewords) % self.n or len(codewords) < self.n):<EOL><INDENT>raise ValueError('<STR_LIT>' %self.n)<EOL><DEDENT>if(np.dtype(codewords[<NUM_LIT:0>]) != int):<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>Num_blocks = int(len(codewords) / self.n)<EOL>decoded_blocks = np.zeros((Num_blocks,self.k),dtype=int)<EOL>codewords = np.reshape(codewords,(Num_blocks,self.n))<EOL>for p in range(Num_blocks):<EOL><INDENT>codeword = codewords[p,:]<EOL>Ureg = np.zeros(self.n)<EOL>S = np.zeros(len(self.G))<EOL>decoded_bits = np.zeros(self.k)<EOL>output = np.zeros(self.n)<EOL>for i in range(<NUM_LIT:0>,self.n): <EOL><INDENT>Ureg = np.roll(Ureg,<NUM_LIT:1>)<EOL>Ureg[<NUM_LIT:0>] = codeword[i]<EOL>S0temp = <NUM_LIT:0><EOL>S[<NUM_LIT:0>] = codeword[i]<EOL>for m in range(len(self.G)):<EOL><INDENT>if(self.G[m] == '<STR_LIT:1>'):<EOL><INDENT>S0temp = S0temp + S[m]<EOL><DEDENT><DEDENT>S0 = S<EOL>S = np.roll(S,<NUM_LIT:1>)<EOL>S[<NUM_LIT:1>] = S0temp % <NUM_LIT:2><EOL><DEDENT>for i in range(<NUM_LIT:0>,self.n): <EOL><INDENT>Stemp = <NUM_LIT:0><EOL>for m in range(<NUM_LIT:1>,len(self.G)):<EOL><INDENT>if(self.G[m] == '<STR_LIT:1>'):<EOL><INDENT>Stemp = Stemp + S[m]<EOL><DEDENT><DEDENT>S = np.roll(S,<NUM_LIT:1>)<EOL>S[<NUM_LIT:1>] = Stemp % <NUM_LIT:2><EOL>and_out = <NUM_LIT:1><EOL>for m in range(<NUM_LIT:1>,len(self.G)):<EOL><INDENT>if(m > <NUM_LIT:1>):<EOL><INDENT>and_out = and_out and ((S[m]+<NUM_LIT:1>) % <NUM_LIT:2>)<EOL><DEDENT>else:<EOL><INDENT>and_out = and_out and S[m]<EOL><DEDENT><DEDENT>output[i] = (and_out + Ureg[len(Ureg)-<NUM_LIT:1>]) % <NUM_LIT:2><EOL>Ureg = np.roll(Ureg,<NUM_LIT:1>)<EOL>Ureg[<NUM_LIT:0>] = <NUM_LIT:0><EOL><DEDENT>decoded_bits = output[<NUM_LIT:0>:self.k].astype(int)<EOL>decoded_blocks[p,:] = decoded_bits<EOL><DEDENT>return np.reshape(decoded_blocks,np.size(decoded_blocks)).astype(int)<EOL>

Decodes a vector of cyclic coded codewords.

parameters
----------
codewords: vector of codewords to be decoded. Numpy array of integers expected.

returns
-------
decoded_blocks: vector of decoded bits

Andrew Smit November 2018


return format(num, '<STR_LIT>'.format(length))<EOL>

Format an integer to binary without the leading '0b'


Pb = np.zeros_like(SNRdB)<EOL>SNR = <NUM_LIT>**(SNRdB/<NUM_LIT>)<EOL>for n,SNRn in enumerate(SNR):<EOL><INDENT>for k in range(dfree,len(Ck)+dfree):<EOL><INDENT>if hard_soft == <NUM_LIT:0>: <EOL><INDENT>Pb[n] += Ck[k-dfree]*hard_Pk(k,R,SNRn,M)<EOL><DEDENT>elif hard_soft == <NUM_LIT:1>: <EOL><INDENT>Pb[n] += Ck[k-dfree]*soft_Pk(k,R,SNRn,M)<EOL><DEDENT>else: <EOL><INDENT>if M == <NUM_LIT:2>:<EOL><INDENT>Pb[n] = Q_fctn(np.sqrt(<NUM_LIT>*SNRn))<EOL><DEDENT>else:<EOL><INDENT>Pb[n] = <NUM_LIT>/np.log2(M)*(<NUM_LIT:1> - <NUM_LIT:1>/np.sqrt(M))*np.gaussQ(np.sqrt(<NUM_LIT:3>*np.log2(M)/(M-<NUM_LIT:1>)*SNRn));<EOL><DEDENT><DEDENT><DEDENT><DEDENT>return Pb<EOL>

Coded bit error probabilty

Convolution coding bit error probability upper bound
according to Ziemer & Peterson 7-16, p. 507

Mark Wickert November 2014

Parameters
----------
R: Code rate
dfree: Free distance of the code
Ck: Weight coefficient
SNRdB: Signal to noise ratio in dB
hard_soft: 0 hard, 1 soft, 2 uncoded
M: M-ary

Examples
--------
>>> import numpy as np
>>> from sk_dsp_comm import fec_conv as fec
>>> import matplotlib.pyplot as plt
>>> SNRdB = np.arange(2,12,.1)
>>> Pb = fec.conv_Pb_bound(1./2,10,[36, 0, 211, 0, 1404, 0, 11633],SNRdB,2)
>>> Pb_1_2 = fec.conv_Pb_bound(1./2,10,[36, 0, 211, 0, 1404, 0, 11633],SNRdB,1)
>>> Pb_3_4 = fec.conv_Pb_bound(3./4,4,[164, 0, 5200, 0, 151211, 0, 3988108],SNRdB,1)
>>> plt.semilogy(SNRdB,Pb)
>>> plt.semilogy(SNRdB,Pb_1_2)
>>> plt.semilogy(SNRdB,Pb_3_4)
>>> plt.axis([2,12,1e-7,1e0])
>>> plt.xlabel(r'$E_b/N_0$ (dB)')
>>> plt.ylabel(r'Symbol Error Probability')
>>> plt.legend(('Uncoded BPSK','R=1/2, K=7, Soft','R=3/4 (punc), K=7, Soft'),loc='best')
>>> plt.grid();
>>> plt.show()

Notes
-----
The code rate R is given by :math:`R_{s} = \\frac{k}{n}`.
Mark Wickert and Andrew Smit 2018


k = int(k)<EOL>if M == <NUM_LIT:2>:<EOL><INDENT>p = Q_fctn(np.sqrt(<NUM_LIT>*R*SNR))<EOL><DEDENT>else:<EOL><INDENT>p = <NUM_LIT>/np.log2(M)*(<NUM_LIT:1> - <NUM_LIT:1.>/np.sqrt(M))*Q_fctn(np.sqrt(<NUM_LIT:3>*R*np.log2(M)/float(M-<NUM_LIT:1>)*SNR))<EOL><DEDENT>Pk = <NUM_LIT:0><EOL>if np.mod(k,<NUM_LIT:2>) == <NUM_LIT:0>:<EOL><INDENT>for e in range(int(k/<NUM_LIT:2>+<NUM_LIT:1>),int(k+<NUM_LIT:1>)):<EOL><INDENT>Pk += float(factorial(k))/(factorial(e)*factorial(k-e))*p**e*(<NUM_LIT:1>-p)**(k-e);<EOL><DEDENT>Pk += <NUM_LIT:1.>/<NUM_LIT:2>*float(factorial(k))/(factorial(int(k/<NUM_LIT:2>))*factorial(int(k-k/<NUM_LIT:2>)))*p**(k/<NUM_LIT:2>)*(<NUM_LIT:1>-p)**(k/<NUM_LIT:2>);<EOL><DEDENT>elif np.mod(k,<NUM_LIT:2>) == <NUM_LIT:1>:<EOL><INDENT>for e in range(int((k+<NUM_LIT:1>)//<NUM_LIT:2>),int(k+<NUM_LIT:1>)):<EOL><INDENT>Pk += factorial(k)/(factorial(e)*factorial(k-e))*p**e*(<NUM_LIT:1>-p)**(k-e);<EOL><DEDENT><DEDENT>return Pk<EOL>

Pk = hard_Pk(k,R,SNR)

Calculates Pk as found in Ziemer & Peterson eq. 7-12, p.505

Mark Wickert and Andrew Smit 2018


if M == <NUM_LIT:2>:<EOL><INDENT>Pk = Q_fctn(np.sqrt(<NUM_LIT>*k*R*SNR))<EOL><DEDENT>else:<EOL><INDENT>Pk = <NUM_LIT>/np.log2(M)*(<NUM_LIT:1> - <NUM_LIT:1.>/np.sqrt(M))*Q_fctn(np.sqrt(<NUM_LIT:3>*k*R*np.log2(M)/float(M-<NUM_LIT:1>)*SNR))<EOL><DEDENT>return Pk<EOL>

Pk = soft_Pk(k,R,SNR)

Calculates Pk as found in Ziemer & Peterson eq. 7-13, p.505

Mark Wickert November 2014


def __init__(self,G = ('<STR_LIT>','<STR_LIT>'), Depth = <NUM_LIT:10>):

self.G_polys = G<EOL>self.constraint_length = len(self.G_polys[<NUM_LIT:0>]) <EOL>self.Nstates = <NUM_LIT:2>**(self.constraint_length-<NUM_LIT:1>) <EOL>self.decision_depth = Depth<EOL>self.input_zero = trellis_nodes(self.Nstates)<EOL>self.input_one = trellis_nodes(self.Nstates)<EOL>self.paths = trellis_paths(self.Nstates,self.decision_depth)<EOL>self.rate = Fraction(<NUM_LIT:1>,len(G))<EOL>if(len(G) == <NUM_LIT:2> or len(G) == <NUM_LIT:3>):<EOL><INDENT>print('<STR_LIT>' %(self.rate))<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL>raise ValueError('<STR_LIT>')<EOL>pass<EOL><DEDENT>for m in range(self.Nstates):<EOL><INDENT>self.input_zero.fn[m] = m<EOL>self.input_one.fn[m] = m<EOL>output0,state0 = self.conv_encoder([<NUM_LIT:0>],<EOL>binary(m,self.constraint_length-<NUM_LIT:1>))<EOL>output1,state1 = self.conv_encoder([<NUM_LIT:1>],<EOL>binary(m,self.constraint_length-<NUM_LIT:1>))<EOL>self.input_zero.tn[m] = int(state0,<NUM_LIT:2>)<EOL>self.input_one.tn[m] = int(state1,<NUM_LIT:2>)<EOL>if(self.rate == Fraction(<NUM_LIT:1>,<NUM_LIT:2>)):<EOL><INDENT>self.input_zero.out_bits[m] = <NUM_LIT:2>*output0[<NUM_LIT:0>] + output0[<NUM_LIT:1>]<EOL>self.input_one.out_bits[m] = <NUM_LIT:2>*output1[<NUM_LIT:0>] + output1[<NUM_LIT:1>]<EOL><DEDENT>elif(self.rate == Fraction(<NUM_LIT:1>,<NUM_LIT:3>)):<EOL><INDENT>self.input_zero.out_bits[m] = <NUM_LIT:4>*output0[<NUM_LIT:0>] + <NUM_LIT:2>*output0[<NUM_LIT:1>] + output0[<NUM_LIT:2>]<EOL>self.input_one.out_bits[m] = <NUM_LIT:4>*output1[<NUM_LIT:0>] + <NUM_LIT:2>*output1[<NUM_LIT:1>] + output1[<NUM_LIT:2>]<EOL><DEDENT><DEDENT>self.branches = trellis_branches(self.Nstates)<EOL>for m in range(self.Nstates):<EOL><INDENT>match_zero_idx = np.where(self.input_zero.tn == m)<EOL>match_one_idx = np.where(self.input_one.tn == m)<EOL>if len(match_zero_idx[<NUM_LIT:0>]) != <NUM_LIT:0>:<EOL><INDENT>self.branches.states1[m] = self.input_zero.fn[match_zero_idx[<NUM_LIT:0>][<NUM_LIT:0>]]<EOL>self.branches.states2[m] = self.input_zero.fn[match_zero_idx[<NUM_LIT:0>][<NUM_LIT:1>]]<EOL>self.branches.bits1[m] = self.input_zero.out_bits[match_zero_idx[<NUM_LIT:0>][<NUM_LIT:0>]]<EOL>self.branches.bits2[m] = self.input_zero.out_bits[match_zero_idx[<NUM_LIT:0>][<NUM_LIT:1>]]<EOL>self.branches.input1[m] = <NUM_LIT:0><EOL>self.branches.input2[m] = <NUM_LIT:0><EOL><DEDENT>elif len(match_one_idx[<NUM_LIT:0>]) != <NUM_LIT:0>:<EOL><INDENT>self.branches.states1[m] = self.input_one.fn[match_one_idx[<NUM_LIT:0>][<NUM_LIT:0>]]<EOL>self.branches.states2[m] = self.input_one.fn[match_one_idx[<NUM_LIT:0>][<NUM_LIT:1>]]<EOL>self.branches.bits1[m] = self.input_one.out_bits[match_one_idx[<NUM_LIT:0>][<NUM_LIT:0>]]<EOL>self.branches.bits2[m] = self.input_one.out_bits[match_one_idx[<NUM_LIT:0>][<NUM_LIT:1>]]<EOL>self.branches.input1[m] = <NUM_LIT:1><EOL>self.branches.input2[m] = <NUM_LIT:1><EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL>exit(<NUM_LIT:1>)<EOL><DEDENT><DEDENT>

cc1 = fec_conv(G = ('111','101'), Depth = 10)
Instantiate a Rate 1/2 or Rate 1/3 convolutional
coder/decoder object. Polys G1 and G2 are entered
as binary strings, e.g,

Rate 1/2
G1 = '111' and G2 = '101' for K = 3 and
G1 = '1111001' and G2 = '1011011' for K = 7.

Rate 1/3
G1 = '111', G2 = '011' and G3 = '101' for K = 3 and
G1 = '1111001', G2 = '1100101' and G3 = '1011011'
for K= 7

The rate will automatically be selected by the number
of G polynomials (only rate 1/2 and 1/3 are available)

Viterbi decoding has a decision depth of Depth.

Data structures than manage the VA are created
upon instantiation via the __init__ method.

Other ideal polynomial considerations (taken from
"Introduction to Digital Communication" Second Edition
by Ziemer and Peterson:

Rate 1/2
K=3 ('111','101')
K=4 ('1111','1101')
K=5 ('11101','10011')
K=6 ('111101','101011')
K=7 ('1111001','1011011')
K=8 ('11111001','10100111')
K=9 ('111101011','101110001')

Rate 1/3
K=3 ('111','111','101')
K=4 ('1111','1101','1011')
K=5 ('11111','11011','10101')
K=6 ('111101','101011','100111')
K=7 ('1111001','1100101','1011011')
K=8 ('11110111','11011001','10010101')

Mark Wickert and Andrew Smit October 2018


if metric_type == '<STR_LIT>':<EOL><INDENT>if np.issubdtype(x.dtype, np.integer):<EOL><INDENT>if x.max() > <NUM_LIT:1> or x.min() < <NUM_LIT:0>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT><DEDENT>cm_present = np.zeros((self.Nstates,<NUM_LIT:1>))<EOL>NS = len(x) <EOL>y = np.zeros(NS-self.decision_depth) <EOL>k = <NUM_LIT:0><EOL>symbolL = self.rate.denominator<EOL>for n in range(<NUM_LIT:0>,NS,symbolL):<EOL><INDENT>cm_past = self.paths.cumulative_metric[:,<NUM_LIT:0>]<EOL>tb_states_temp = self.paths.traceback_states[:,:-<NUM_LIT:1>].copy()<EOL>tb_bits_temp = self.paths.traceback_bits[:,:-<NUM_LIT:1>].copy()<EOL>for m in range(self.Nstates):<EOL><INDENT>d1 = self.bm_calc(self.branches.bits1[m],<EOL>x[n:n+symbolL],metric_type,<EOL>quant_level)<EOL>d1 = d1 + cm_past[self.branches.states1[m]]<EOL>d2 = self.bm_calc(self.branches.bits2[m],<EOL>x[n:n+symbolL],metric_type,<EOL>quant_level)<EOL>d2 = d2 + cm_past[self.branches.states2[m]]<EOL>if d1 <= d2: <EOL><INDENT>cm_present[m] = d1<EOL>self.paths.traceback_states[m,:] = np.hstack((self.branches.states1[m],<EOL>tb_states_temp[int(self.branches.states1[m]),:]))<EOL>self.paths.traceback_bits[m,:] = np.hstack((self.branches.input1[m],<EOL>tb_bits_temp[int(self.branches.states1[m]),:]))<EOL><DEDENT>else:<EOL><INDENT>cm_present[m] = d2<EOL>self.paths.traceback_states[m,:] = np.hstack((self.branches.states2[m],<EOL>tb_states_temp[int(self.branches.states2[m]),:]))<EOL>self.paths.traceback_bits[m,:] = np.hstack((self.branches.input2[m],<EOL>tb_bits_temp[int(self.branches.states2[m]),:]))<EOL><DEDENT><DEDENT>self.paths.cumulative_metric = np.hstack((cm_present, <EOL>self.paths.cumulative_metric[:,:-<NUM_LIT:1>]))<EOL>min_metric = min(self.paths.cumulative_metric[:,<NUM_LIT:0>])<EOL>min_idx = np.where(self.paths.cumulative_metric[:,<NUM_LIT:0>] == min_metric)<EOL>if n >= symbolL*self.decision_depth-symbolL:  <EOL><INDENT>y[k] = self.paths.traceback_bits[min_idx[<NUM_LIT:0>][<NUM_LIT:0>],-<NUM_LIT:1>]<EOL>k += <NUM_LIT:1><EOL><DEDENT><DEDENT>y = y[:k] <EOL>return y<EOL>

A method which performs Viterbi decoding of noisy bit stream,
taking as input soft bit values centered on +/-1 and returning
hard decision 0/1 bits.

Parameters
----------
x: Received noisy bit values centered on +/-1 at one sample per bit
metric_type:
    'hard' - Hard decision metric. Expects binary or 0/1 input values.
    'unquant' - unquantized soft decision decoding. Expects +/-1
        input values.
    'soft' - soft decision decoding.
quant_level: The quantization level for soft decoding. Expected
input values between 0 and 2^quant_level-1. 0 represents the most
confident 0 and 2^quant_level-1 represents the most confident 1.
Only used for 'soft' metric type.

Returns
-------
y: Decoded 0/1 bit stream

Examples
--------
>>> import numpy as np
>>> from numpy.random import randint
>>> import sk_dsp_comm.fec_conv as fec
>>> import sk_dsp_comm.digitalcom as dc
>>> import matplotlib.pyplot as plt
>>> # Soft decision rate 1/2 simulation
>>> N_bits_per_frame = 10000
>>> EbN0 = 4
>>> total_bit_errors = 0
>>> total_bit_count = 0
>>> cc1 = fec.fec_conv(('11101','10011'),25)
>>> # Encode with shift register starting state of '0000'
>>> state = '0000'
>>> while total_bit_errors < 100:
>>>     # Create 100000 random 0/1 bits
>>>     x = randint(0,2,N_bits_per_frame)
>>>     y,state = cc1.conv_encoder(x,state)
>>>     # Add channel noise to bits, include antipodal level shift to [-1,1]
>>>     yn_soft = dc.cpx_AWGN(2*y-1,EbN0-3,1) # Channel SNR is 3 dB less for rate 1/2
>>>     yn_hard = ((np.sign(yn_soft.real)+1)/2).astype(int)
>>>     z = cc1.viterbi_decoder(yn_hard,'hard')
>>>     # Count bit errors
>>>     bit_count, bit_errors = dc.bit_errors(x,z)
>>>     total_bit_errors += bit_errors
>>>     total_bit_count += bit_count
>>>     print('Bits Received = %d, Bit errors = %d, BEP = %1.2e' %\
            (total_bit_count, total_bit_errors,\
            total_bit_errors/total_bit_count))
>>> print('*****************************************************')
>>> print('Bits Received = %d, Bit errors = %d, BEP = %1.2e' %\
        (total_bit_count, total_bit_errors,\
        total_bit_errors/total_bit_count))
Rate 1/2 Object
kmax =  0, taumax = 0
Bits Received = 9976, Bit errors = 77, BEP = 7.72e-03
kmax =  0, taumax = 0
Bits Received = 19952, Bit errors = 175, BEP = 8.77e-03
*****************************************************
Bits Received = 19952, Bit errors = 175, BEP = 8.77e-03

>>> # Consider the trellis traceback after the sim completes
>>> cc1.traceback_plot()
>>> plt.show()

>>> # Compare a collection of simulation results with soft decision
>>> # bounds
>>> SNRdB = np.arange(0,12,.1)
>>> Pb_uc = fec.conv_Pb_bound(1/3,7,[4, 12, 20, 72, 225],SNRdB,2)
>>> Pb_s_third_3 = fec.conv_Pb_bound(1/3,8,[3, 0, 15],SNRdB,1)
>>> Pb_s_third_4 = fec.conv_Pb_bound(1/3,10,[6, 0, 6, 0],SNRdB,1)
>>> Pb_s_third_5 = fec.conv_Pb_bound(1/3,12,[12, 0, 12, 0, 56],SNRdB,1)
>>> Pb_s_third_6 = fec.conv_Pb_bound(1/3,13,[1, 8, 26, 20, 19, 62],SNRdB,1)
>>> Pb_s_third_7 = fec.conv_Pb_bound(1/3,14,[1, 0, 20, 0, 53, 0, 184],SNRdB,1)
>>> Pb_s_third_8 = fec.conv_Pb_bound(1/3,16,[1, 0, 24, 0, 113, 0, 287, 0],SNRdB,1)
>>> Pb_s_half = fec.conv_Pb_bound(1/2,7,[4, 12, 20, 72, 225],SNRdB,1)
>>> plt.figure(figsize=(5,5))
>>> plt.semilogy(SNRdB,Pb_uc)
>>> plt.semilogy(SNRdB,Pb_s_third_3,'--')
>>> plt.semilogy(SNRdB,Pb_s_third_4,'--')
>>> plt.semilogy(SNRdB,Pb_s_third_5,'g')
>>> plt.semilogy(SNRdB,Pb_s_third_6,'--')
>>> plt.semilogy(SNRdB,Pb_s_third_7,'--')
>>> plt.semilogy(SNRdB,Pb_s_third_8,'--')
>>> plt.semilogy([0,1,2,3,4,5],[9.08e-02,2.73e-02,6.52e-03,\
                        8.94e-04,8.54e-05,5e-6],'gs')
>>> plt.axis([0,12,1e-7,1e0])
>>> plt.title(r'Soft Decision Rate 1/2 Coding Measurements')
>>> plt.xlabel(r'$E_b/N_0$ (dB)')
>>> plt.ylabel(r'Symbol Error Probability')
>>> plt.legend(('Uncoded BPSK','R=1/3, K=3, Soft',\
            'R=1/3, K=4, Soft','R=1/3, K=5, Soft',\
            'R=1/3, K=6, Soft','R=1/3, K=7, Soft',\
            'R=1/3, K=8, Soft','R=1/3, K=5, Sim', \
            'Simulation'),loc='upper right')
>>> plt.grid();
>>> plt.show()

>>> # Hard decision rate 1/3 simulation
>>> N_bits_per_frame = 10000
>>> EbN0 = 3
>>> total_bit_errors = 0
>>> total_bit_count = 0
>>> cc2 = fec.fec_conv(('11111','11011','10101'),25)
>>> # Encode with shift register starting state of '0000'
>>> state = '0000'
>>> while total_bit_errors < 100:
>>>     # Create 100000 random 0/1 bits
>>>     x = randint(0,2,N_bits_per_frame)
>>>     y,state = cc2.conv_encoder(x,state)
>>>     # Add channel noise to bits, include antipodal level shift to [-1,1]
>>>     yn_soft = dc.cpx_AWGN(2*y-1,EbN0-10*np.log10(3),1) # Channel SNR is 10*log10(3) dB less
>>>     yn_hard = ((np.sign(yn_soft.real)+1)/2).astype(int)
>>>     z = cc2.viterbi_decoder(yn_hard.real,'hard')
>>>     # Count bit errors
>>>     bit_count, bit_errors = dc.bit_errors(x,z)
>>>     total_bit_errors += bit_errors
>>>     total_bit_count += bit_count
>>>     print('Bits Received = %d, Bit errors = %d, BEP = %1.2e' %\
            (total_bit_count, total_bit_errors,\
            total_bit_errors/total_bit_count))
>>> print('*****************************************************')
>>> print('Bits Received = %d, Bit errors = %d, BEP = %1.2e' %\
        (total_bit_count, total_bit_errors,\
        total_bit_errors/total_bit_count))
Rate 1/3 Object
kmax =  0, taumax = 0
Bits Received = 9976, Bit errors = 251, BEP = 2.52e-02
*****************************************************
Bits Received = 9976, Bit errors = 251, BEP = 2.52e-02

>>> # Compare a collection of simulation results with hard decision
>>> # bounds
>>> SNRdB = np.arange(0,12,.1)
>>> Pb_uc = fec.conv_Pb_bound(1/3,7,[4, 12, 20, 72, 225],SNRdB,2)
>>> Pb_s_third_3_hard = fec.conv_Pb_bound(1/3,8,[3, 0, 15, 0, 58, 0, 201, 0],SNRdB,0)
>>> Pb_s_third_5_hard = fec.conv_Pb_bound(1/3,12,[12, 0, 12, 0, 56, 0, 320, 0],SNRdB,0)
>>> Pb_s_third_7_hard = fec.conv_Pb_bound(1/3,14,[1, 0, 20, 0, 53, 0, 184],SNRdB,0)
>>> Pb_s_third_5_hard_sim = np.array([8.94e-04,1.11e-04,8.73e-06])
>>> plt.figure(figsize=(5,5))
>>> plt.semilogy(SNRdB,Pb_uc)
>>> plt.semilogy(SNRdB,Pb_s_third_3_hard,'r--')
>>> plt.semilogy(SNRdB,Pb_s_third_5_hard,'g--')
>>> plt.semilogy(SNRdB,Pb_s_third_7_hard,'k--')
>>> plt.semilogy(np.array([5,6,7]),Pb_s_third_5_hard_sim,'sg')
>>> plt.axis([0,12,1e-7,1e0])
>>> plt.title(r'Hard Decision Rate 1/3 Coding Measurements')
>>> plt.xlabel(r'$E_b/N_0$ (dB)')
>>> plt.ylabel(r'Symbol Error Probability')
>>> plt.legend(('Uncoded BPSK','R=1/3, K=3, Hard',\
            'R=1/3, K=5, Hard', 'R=1/3, K=7, Hard',\
            ),loc='upper right')
>>> plt.grid();
>>> plt.show()

>>> # Show the traceback for the rate 1/3 hard decision case
>>> cc2.traceback_plot()


def bm_calc(self,ref_code_bits, rec_code_bits, metric_type, quant_level):

distance = <NUM_LIT:0><EOL>if metric_type == '<STR_LIT>': <EOL><INDENT>bits = binary(int(ref_code_bits),self.rate.denominator)<EOL>for k in range(len(bits)):<EOL><INDENT>ref_bit = (<NUM_LIT:2>**quant_level-<NUM_LIT:1>)*int(bits[k],<NUM_LIT:2>)<EOL>distance += (int(rec_code_bits[k]) - ref_bit)**<NUM_LIT:2><EOL><DEDENT><DEDENT>elif metric_type == '<STR_LIT>': <EOL><INDENT>bits = binary(int(ref_code_bits),self.rate.denominator)<EOL>for k in range(len(rec_code_bits)):<EOL><INDENT>distance += abs(rec_code_bits[k] - int(bits[k]))<EOL><DEDENT><DEDENT>elif metric_type == '<STR_LIT>': <EOL><INDENT>bits = binary(int(ref_code_bits),self.rate.denominator)<EOL>for k in range(len(bits)):<EOL><INDENT>distance += (float(rec_code_bits[k])-float(bits[k]))**<NUM_LIT:2><EOL><DEDENT><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL>raise ValueError('<STR_LIT>')<EOL><DEDENT>return distance<EOL>

distance = bm_calc(ref_code_bits, rec_code_bits, metric_type)
Branch metrics calculation

Mark Wickert and Andrew Smit October 2018


output = []<EOL>if(self.rate == Fraction(<NUM_LIT:1>,<NUM_LIT:2>)):<EOL><INDENT>for n in range(len(input)):<EOL><INDENT>u1 = int(input[n])<EOL>u2 = int(input[n])<EOL>for m in range(<NUM_LIT:1>,self.constraint_length):<EOL><INDENT>if int(self.G_polys[<NUM_LIT:0>][m]) == <NUM_LIT:1>: <EOL><INDENT>u1 = u1 ^ int(state[m-<NUM_LIT:1>])<EOL><DEDENT>if int(self.G_polys[<NUM_LIT:1>][m]) == <NUM_LIT:1>: <EOL><INDENT>u2 = u2 ^ int(state[m-<NUM_LIT:1>])<EOL><DEDENT><DEDENT>output = np.hstack((output, [u1, u2]))<EOL>state = bin(int(input[n]))[-<NUM_LIT:1>] + state[:-<NUM_LIT:1>]<EOL><DEDENT><DEDENT>elif(self.rate == Fraction(<NUM_LIT:1>,<NUM_LIT:3>)):<EOL><INDENT>for n in range(len(input)):<EOL><INDENT>if(int(self.G_polys[<NUM_LIT:0>][<NUM_LIT:0>]) == <NUM_LIT:1>):<EOL><INDENT>u1 = int(input[n])<EOL><DEDENT>else:<EOL><INDENT>u1 = <NUM_LIT:0><EOL><DEDENT>if(int(self.G_polys[<NUM_LIT:1>][<NUM_LIT:0>]) == <NUM_LIT:1>):<EOL><INDENT>u2 = int(input[n])<EOL><DEDENT>else:<EOL><INDENT>u2 = <NUM_LIT:0><EOL><DEDENT>if(int(self.G_polys[<NUM_LIT:2>][<NUM_LIT:0>]) == <NUM_LIT:1>):<EOL><INDENT>u3 = int(input[n])<EOL><DEDENT>else:<EOL><INDENT>u3 = <NUM_LIT:0><EOL><DEDENT>for m in range(<NUM_LIT:1>,self.constraint_length):<EOL><INDENT>if int(self.G_polys[<NUM_LIT:0>][m]) == <NUM_LIT:1>: <EOL><INDENT>u1 = u1 ^ int(state[m-<NUM_LIT:1>])<EOL><DEDENT>if int(self.G_polys[<NUM_LIT:1>][m]) == <NUM_LIT:1>: <EOL><INDENT>u2 = u2 ^ int(state[m-<NUM_LIT:1>])<EOL><DEDENT>if int(self.G_polys[<NUM_LIT:2>][m]) == <NUM_LIT:1>: <EOL><INDENT>u3 = u3 ^ int(state[m-<NUM_LIT:1>])<EOL><DEDENT><DEDENT>output = np.hstack((output, [u1, u2, u3]))<EOL>state = bin(int(input[n]))[-<NUM_LIT:1>] + state[:-<NUM_LIT:1>]<EOL><DEDENT><DEDENT>return output, state<EOL>

output, state = conv_encoder(input,state)
We get the 1/2 or 1/3 rate from self.rate
Polys G1 and G2 are entered as binary strings, e.g,
G1 = '111' and G2 = '101' for K = 3
G1 = '1011011' and G2 = '1111001' for K = 7
G3 is also included for rate 1/3
Input state as a binary string of length K-1, e.g., '00' or '0000000'
e.g., state = '00' for K = 3
e.g., state = '000000' for K = 7
Mark Wickert and Andrew Smit 2018


def puncture(self,code_bits,puncture_pattern = ('<STR_LIT>','<STR_LIT>')):

<EOL>L_pp = len(puncture_pattern[<NUM_LIT:0>])<EOL>N_codewords = int(np.floor(len(code_bits)/float(<NUM_LIT:2>)))<EOL>if <NUM_LIT:2>*N_codewords != len(code_bits):<EOL><INDENT>warnings.warn('<STR_LIT>')<EOL>warnings.warn('<STR_LIT>')<EOL>code_bits = code_bits[:<NUM_LIT:2>*N_codewords]<EOL><DEDENT>x_G1 = code_bits.reshape(N_codewords,<NUM_LIT:2>).take([<NUM_LIT:0>],<EOL>axis=<NUM_LIT:1>).reshape(<NUM_LIT:1>,N_codewords).flatten()<EOL>x_G2 = code_bits.reshape(N_codewords,<NUM_LIT:2>).take([<NUM_LIT:1>],<EOL>axis=<NUM_LIT:1>).reshape(<NUM_LIT:1>,N_codewords).flatten()<EOL>N_punct_periods = int(np.floor(N_codewords/float(L_pp)))<EOL>if L_pp*N_punct_periods != N_codewords:<EOL><INDENT>warnings.warn('<STR_LIT>' % L_pp)<EOL>warnings.warn('<STR_LIT>')<EOL>x_G1 = x_G1[:L_pp*N_punct_periods]<EOL>x_G2 = x_G2[:L_pp*N_punct_periods]<EOL><DEDENT>g1_pp1 = [k for k,g1 in enumerate(puncture_pattern[<NUM_LIT:0>]) if g1 == '<STR_LIT:1>']<EOL>g2_pp1 = [k for k,g2 in enumerate(puncture_pattern[<NUM_LIT:1>]) if g2 == '<STR_LIT:1>']<EOL>N_pp = len(g1_pp1)<EOL>y_G1 = x_G1.reshape(N_punct_periods,L_pp).take(g1_pp1,<EOL>axis=<NUM_LIT:1>).reshape(N_pp*N_punct_periods,<NUM_LIT:1>)<EOL>y_G2 = x_G2.reshape(N_punct_periods,L_pp).take(g2_pp1,<EOL>axis=<NUM_LIT:1>).reshape(N_pp*N_punct_periods,<NUM_LIT:1>)<EOL>y = np.hstack((y_G1,y_G2)).reshape(<NUM_LIT:1>,<NUM_LIT:2>*N_pp*N_punct_periods).flatten()<EOL>return y<EOL>

Apply puncturing to the serial bits produced by convolutionally
encoding.

:param code_bits:
:param puncture_pattern:
:return:

Examples
--------
This example uses the following puncture matrix:

.. math::

   \\begin{align*}
       \\mathbf{A} = \\begin{bmatrix}
        1 & 1 & 0 \\\\
        1 & 0 & 1
        \\end{bmatrix}
   \\end{align*}

The upper row operates on the outputs for the :math:`G_{1}` polynomial and the lower row operates on the outputs of
the  :math:`G_{2}`  polynomial.

>>> import numpy as np
>>> from sk_dsp_comm.fec_conv import fec_conv
>>> cc = fec_conv(('101','111'))
>>> x = np.array([0, 0, 1, 1, 1, 0, 0, 0, 0, 0])
>>> state = '00'
>>> y, state = cc.conv_encoder(x, state)
>>> cc.puncture(y, ('110','101'))
array([ 0.,  0.,  0.,  1.,  1.,  0.,  0.,  0.,  1.,  1.,  0.,  0.])


def depuncture(self,soft_bits,puncture_pattern = ('<STR_LIT>','<STR_LIT>'),<EOL>erase_value = <NUM_LIT>):

<EOL>L_pp = len(puncture_pattern[<NUM_LIT:0>])<EOL>L_pp1 = len([g1 for g1 in puncture_pattern[<NUM_LIT:0>] if g1 == '<STR_LIT:1>'])<EOL>L_pp0 = len([g1 for g1 in puncture_pattern[<NUM_LIT:0>] if g1 == '<STR_LIT:0>'])<EOL>N_softwords = int(np.floor(len(soft_bits)/float(<NUM_LIT:2>)))<EOL>if <NUM_LIT:2>*N_softwords != len(soft_bits):<EOL><INDENT>warnings.warn('<STR_LIT>')<EOL>warnings.warn('<STR_LIT>')<EOL>soft_bits = soft_bits[:<NUM_LIT:2>*N_softwords]<EOL><DEDENT>x_G1 = soft_bits.reshape(N_softwords,<NUM_LIT:2>).take([<NUM_LIT:0>],<EOL>axis=<NUM_LIT:1>).reshape(<NUM_LIT:1>,N_softwords).flatten()<EOL>x_G2 = soft_bits.reshape(N_softwords,<NUM_LIT:2>).take([<NUM_LIT:1>],<EOL>axis=<NUM_LIT:1>).reshape(<NUM_LIT:1>,N_softwords).flatten()<EOL>N_punct_periods = int(np.floor(N_softwords/float(L_pp1)))<EOL>if L_pp1*N_punct_periods != N_softwords:<EOL><INDENT>warnings.warn('<STR_LIT>' % L_pp1)<EOL>warnings.warn('<STR_LIT>')<EOL>warnings.warn('<STR_LIT>')<EOL>x_G1 = x_G1[:L_pp1*N_punct_periods]<EOL>x_G2 = x_G2[:L_pp1*N_punct_periods]<EOL><DEDENT>x_G1 = x_G1.reshape(N_punct_periods,L_pp1)<EOL>x_G2 = x_G2.reshape(N_punct_periods,L_pp1)<EOL>g1_pp1 = [k for k,g1 in enumerate(puncture_pattern[<NUM_LIT:0>]) if g1 == '<STR_LIT:1>']<EOL>g1_pp0 = [k for k,g1 in enumerate(puncture_pattern[<NUM_LIT:0>]) if g1 == '<STR_LIT:0>']<EOL>g2_pp1 = [k for k,g2 in enumerate(puncture_pattern[<NUM_LIT:1>]) if g2 == '<STR_LIT:1>']<EOL>g2_pp0 = [k for k,g2 in enumerate(puncture_pattern[<NUM_LIT:1>]) if g2 == '<STR_LIT:0>']<EOL>x_E = erase_value*np.ones((N_punct_periods,L_pp0))<EOL>y_G1 = np.hstack((x_G1,x_E))<EOL>y_G2 = np.hstack((x_G2,x_E))<EOL>[g1_pp1.append(val) for idx,val in enumerate(g1_pp0)]<EOL>g1_comp = list(zip(g1_pp1,list(range(L_pp))))<EOL>g1_comp.sort()<EOL>G1_col_permute = [g1_comp[idx][<NUM_LIT:1>] for idx in range(L_pp)]<EOL>[g2_pp1.append(val) for idx,val in enumerate(g2_pp0)]<EOL>g2_comp = list(zip(g2_pp1,list(range(L_pp))))<EOL>g2_comp.sort()<EOL>G2_col_permute = [g2_comp[idx][<NUM_LIT:1>] for idx in range(L_pp)]<EOL>y = np.hstack((y_G1[:,G1_col_permute].reshape(L_pp*N_punct_periods,<NUM_LIT:1>),<EOL>y_G2[:,G2_col_permute].reshape(L_pp*N_punct_periods,<EOL><NUM_LIT:1>))).reshape(<NUM_LIT:1>,<NUM_LIT:2>*L_pp*N_punct_periods).flatten()<EOL>return y<EOL>

Apply de-puncturing to the soft bits coming from the channel. Erasure bits
are inserted to return the soft bit values back to a form that can be
Viterbi decoded.

:param soft_bits:
:param puncture_pattern:
:param erase_value:
:return:

Examples
--------
This example uses the following puncture matrix:

.. math::

   \\begin{align*}
       \\mathbf{A} = \\begin{bmatrix}
        1 & 1 & 0 \\\\
        1 & 0 & 1
        \\end{bmatrix}
   \\end{align*}

The upper row operates on the outputs for the :math:`G_{1}` polynomial and the lower row operates on the outputs of
the  :math:`G_{2}`  polynomial.

>>> import numpy as np
>>> from sk_dsp_comm.fec_conv import fec_conv
>>> cc = fec_conv(('101','111'))
>>> x = np.array([0, 0, 1, 1, 1, 0, 0, 0, 0, 0])
>>> state = '00'
>>> y, state = cc.conv_encoder(x, state)
>>> yp = cc.puncture(y, ('110','101'))
>>> cc.depuncture(yp, ('110', '101'), 1)
array([ 0., 0., 0., 1., 1., 1., 1., 0., 0., 1., 1., 0., 1., 1., 0., 1., 1., 0.]


branches_from = self.branches<EOL>plt.figure(figsize=fsize)<EOL>plt.plot(<NUM_LIT:0>,<NUM_LIT:0>,'<STR_LIT:.>')<EOL>plt.axis([-<NUM_LIT>, <NUM_LIT>, -(self.Nstates-<NUM_LIT:1>)-<NUM_LIT>, <NUM_LIT>])<EOL>for m in range(self.Nstates):<EOL><INDENT>if branches_from.input1[m] == <NUM_LIT:0>:<EOL><INDENT>plt.plot([<NUM_LIT:0>, <NUM_LIT:1>],[-branches_from.states1[m], -m],'<STR_LIT:b>')<EOL>plt.plot([<NUM_LIT:0>, <NUM_LIT:1>],[-branches_from.states1[m], -m],'<STR_LIT>')<EOL><DEDENT>if branches_from.input2[m] == <NUM_LIT:0>:<EOL><INDENT>plt.plot([<NUM_LIT:0>, <NUM_LIT:1>],[-branches_from.states2[m], -m],'<STR_LIT:b>')<EOL>plt.plot([<NUM_LIT:0>, <NUM_LIT:1>],[-branches_from.states2[m], -m],'<STR_LIT>')<EOL><DEDENT>if branches_from.input1[m] == <NUM_LIT:1>:<EOL><INDENT>plt.plot([<NUM_LIT:0>, <NUM_LIT:1>],[-branches_from.states1[m], -m],'<STR_LIT:g>')<EOL>plt.plot([<NUM_LIT:0>, <NUM_LIT:1>],[-branches_from.states1[m], -m],'<STR_LIT>')<EOL><DEDENT>if branches_from.input2[m] == <NUM_LIT:1>:<EOL><INDENT>plt.plot([<NUM_LIT:0>, <NUM_LIT:1>],[-branches_from.states2[m], -m],'<STR_LIT:g>')<EOL>plt.plot([<NUM_LIT:0>, <NUM_LIT:1>],[-branches_from.states2[m], -m],'<STR_LIT>')<EOL><DEDENT><DEDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>msg = '<STR_LIT>' %(self.rate, int(np.ceil(np.log2(self.Nstates)+<NUM_LIT:1>)))<EOL>plt.title(msg)<EOL>

Plots a trellis diagram of the possible state transitions.

Parameters
----------
fsize : Plot size for matplotlib.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm.fec_conv import fec_conv
>>> cc = fec_conv()
>>> cc.trellis_plot()
>>> plt.show()


traceback_states = self.paths.traceback_states<EOL>plt.figure(figsize=fsize)<EOL>plt.axis([-self.decision_depth+<NUM_LIT:1>, <NUM_LIT:0>, <EOL>-(self.Nstates-<NUM_LIT:1>)-<NUM_LIT:0.5>, <NUM_LIT:0.5>])<EOL>M,N = traceback_states.shape<EOL>traceback_states = -traceback_states[:,::-<NUM_LIT:1>]<EOL>plt.plot(range(-(N-<NUM_LIT:1>),<NUM_LIT:0>+<NUM_LIT:1>),traceback_states.T)<EOL>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>' % self.Nstates)<EOL>plt.grid()<EOL>

Plots a path of the possible last 4 states.

Parameters
----------
fsize : Plot size for matplotlib.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm.fec_conv import fec_conv
>>> from sk_dsp_comm import digitalcom as dc
>>> import numpy as np
>>> cc = fec_conv()
>>> x = np.random.randint(0,2,100)
>>> state = '00'
>>> y,state = cc.conv_encoder(x,state)
>>> # Add channel noise to bits translated to +1/-1
>>> yn = dc.cpx_AWGN(2*y-1,5,1) # SNR = 5 dB
>>> # Translate noisy +1/-1 bits to soft values on [0,7]
>>> yn = (yn.real+1)/2*7
>>> z = cc.viterbi_decoder(yn)
>>> cc.traceback_plot()
>>> plt.show()


devices = {}<EOL>pA = pyaudio.PyAudio()<EOL>device_string = str()<EOL>for k in range(pA.get_device_count()):<EOL><INDENT>dev = pA.get_device_info_by_index(k)<EOL>devices[k] = {'<STR_LIT:name>': dev['<STR_LIT:name>'], '<STR_LIT>': dev['<STR_LIT>'], '<STR_LIT>': dev['<STR_LIT>']}<EOL>device_string += '<STR_LIT>' %(k,dev['<STR_LIT:name>'],dev['<STR_LIT>'],dev['<STR_LIT>'])<EOL><DEDENT>logger.debug(device_string)<EOL>return devices<EOL>

Display available input and output audio devices along with their
port indices.

:return:  Dictionary whose keys are the device index, the number of inputs and outputs, and their names.
:rtype: dict


def __init__(self, stream_callback, in_idx = <NUM_LIT:1>, out_idx = <NUM_LIT:4>, frame_length = <NUM_LIT>, <EOL>fs = <NUM_LIT>, Tcapture = <NUM_LIT:0>, sleep_time = <NUM_LIT:0.1>):

self.in_idx = in_idx<EOL>self.out_idx = out_idx<EOL>self.in_out_check()<EOL>self.frame_length = frame_length<EOL>self.fs = fs<EOL>self.sleep_time = sleep_time<EOL>self.stream_callback = stream_callback<EOL>self.p = pyaudio.PyAudio()<EOL>self.stream_data = False<EOL>self.capture_sample_count = <NUM_LIT:0><EOL>self.data_capture = list()<EOL>self.data_capture_left = list()<EOL>self.data_capture_right = list()<EOL>self.Tcapture = Tcapture<EOL>self.Ncapture = int(self.fs*self.Tcapture)<EOL>self.left_in = np.zeros(frame_length)<EOL>self.right_in = np.zeros(frame_length)<EOL>self.out = np.zeros(frame_length*<NUM_LIT:2>)<EOL>self.interactiveFG = <NUM_LIT:0><EOL>self.print_when_done = <NUM_LIT:1><EOL>self.DSP_tic = list()<EOL>self.DSP_toc = list()<EOL>

:param stream_callback: Function that will provide the callback functionality
:param in_idx: Input device id
:param out_idx: Output device id
:param frame_length:
:param fs: Sampling frequency
:param Tcapture: Time to capture (seconds)
:param sleep_time:


devices = available_devices()<EOL>if not self.in_idx in devices:<EOL><INDENT>raise OSError("<STR_LIT>")<EOL><DEDENT>in_check = devices[self.in_idx]<EOL>if not self.out_idx in devices:<EOL><INDENT>raise OSError("<STR_LIT>")<EOL><DEDENT>out_check = devices[self.out_idx]<EOL>if((in_check['<STR_LIT>'] == <NUM_LIT:0>) and (out_check['<STR_LIT>']==<NUM_LIT:0>)):<EOL><INDENT>raise StandardError('<STR_LIT>')<EOL><DEDENT>elif(in_check['<STR_LIT>'] == <NUM_LIT:0>):<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>elif(out_check['<STR_LIT>'] == <NUM_LIT:0>):<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>return True<EOL>

Checks the input and output to see if they are valid


def interactive_stream(self,Tsec = <NUM_LIT:2>, numChan = <NUM_LIT:1>):

self.Tsec = Tsec<EOL>self.numChan = numChan<EOL>self.interactiveFG = <NUM_LIT:1><EOL>self.play = interactive(self.interaction,Stream = ToggleButtons(<EOL>options=['<STR_LIT>', '<STR_LIT>'],<EOL>description = '<STR_LIT:U+0020>',<EOL>value = '<STR_LIT>') )<EOL>display(self.play)<EOL>

Stream audio with start and stop radio buttons

Interactive stream is designed for streaming audio through this object using
a callback function. This stream is threaded, so it can be used with ipywidgets.
Click on the "Start Streaming" button to start streaming and click on "Stop Streaming"
button to stop streaming.

Parameters
----------

Tsec : stream time in seconds if Tsec > 0. If Tsec = 0, then stream goes to infinite
mode. When in infinite mode, the "Stop Streaming" radio button or Tsec.stop() can be
used to stop the stream.

numChan : number of channels. Use 1 for mono and 2 for stereo.


def thread_stream(self,Tsec = <NUM_LIT:2>,numChan = <NUM_LIT:1>):

def stream_thread(time,channel):<EOL><INDENT>self.stream(Tsec=time,numChan = channel)<EOL><DEDENT>t = Thread(target=stream_thread, args=(Tsec,numChan,))<EOL>t.start()<EOL>

Stream audio in a thread using callback. The stream is threaded, so widgets can be
used simultaneously during stream.

Parameters
----------

Tsec : stream time in seconds if Tsec > 0. If Tsec = 0, then stream goes to infinite
mode. When in infinite mode, Tsec.stop() can be used to stop the stream.

numChan : number of channels. Use 1 for mono and 2 for stereo.


def stream(self,Tsec = <NUM_LIT:2>,numChan = <NUM_LIT:1>):

self.Tsec = Tsec<EOL>self.numChan = numChan<EOL>self.N_samples = int(self.fs*Tsec)<EOL>self.data_capture = []<EOL>self.data_capture_left = []<EOL>self.data_capture_right = []<EOL>self.capture_sample_count = <NUM_LIT:0><EOL>self.DSP_tic = []<EOL>self.DSP_toc = []<EOL>self.start_time = time.time()<EOL>self.stop_stream = False<EOL>stream = self.p.open(format=pyaudio.paInt16,<EOL>channels=numChan,<EOL>rate=self.fs,<EOL>input=True,<EOL>output=True,<EOL>input_device_index = self.in_idx,<EOL>output_device_index = self.out_idx,<EOL>frames_per_buffer = self.frame_length,<EOL>stream_callback=self.stream_callback)<EOL>stream.start_stream()<EOL>if(Tsec == <NUM_LIT:0>):<EOL><INDENT>while stream.is_active():<EOL><INDENT>if self.stop_stream:<EOL><INDENT>stream.stop_stream()<EOL><DEDENT>time.sleep(self.sleep_time)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>while stream.is_active():<EOL><INDENT>if self.capture_sample_count >= self.N_samples:<EOL><INDENT>stream.stop_stream()<EOL><DEDENT>if self.stop_stream:<EOL><INDENT>stream.stop_stream()<EOL><DEDENT>time.sleep(self.sleep_time)<EOL><DEDENT><DEDENT>stream.stop_stream()<EOL>stream.close()<EOL>self.p.terminate()<EOL>self.stream_data = True<EOL>if(self.interactiveFG):<EOL><INDENT>self.play.children[<NUM_LIT:0>].value = '<STR_LIT>'<EOL><DEDENT>else:<EOL><INDENT>if(self.print_when_done == <NUM_LIT:1>):<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT><DEDENT>

Stream audio using callback

Parameters
----------

Tsec : stream time in seconds if Tsec > 0. If Tsec = 0, then stream goes to infinite
mode. When in infinite mode, Tsec.stop() can be used to stop the stream.

numChan : number of channels. Use 1 for mono and 2 for stereo.


self.stop_stream = True<EOL>


self.capture_sample_count += len(new_data)<EOL>if self.Tcapture > <NUM_LIT:0>:<EOL><INDENT>self.data_capture = np.hstack((self.data_capture,new_data))<EOL>if (self.Tcapture > <NUM_LIT:0>) and (len(self.data_capture) > self.Ncapture):<EOL><INDENT>self.data_capture = self.data_capture[-self.Ncapture:]<EOL><DEDENT><DEDENT>

Append new samples to the data_capture array and increment the sample counter
If length reaches Tcapture, then the newest samples will be kept. If Tcapture = 0
then new values are not appended to the data_capture array.


self.capture_sample_count = self.capture_sample_count + len(new_data_left) + len(new_data_right)<EOL>if self.Tcapture > <NUM_LIT:0>:<EOL><INDENT>self.data_capture_left = np.hstack((self.data_capture_left,new_data_left))<EOL>self.data_capture_right = np.hstack((self.data_capture_right,new_data_right))<EOL>if (len(self.data_capture_left) > self.Ncapture):<EOL><INDENT>self.data_capture_left = self.data_capture_left[-self.Ncapture:]<EOL><DEDENT>if (len(self.data_capture_right) > self.Ncapture):<EOL><INDENT>self.data_capture_right = self.data_capture_right[-self.Ncapture:]<EOL><DEDENT><DEDENT>

Append new samples to the data_capture_left array and the data_capture_right
array and increment the sample counter. If length reaches Tcapture, then the
newest samples will be kept. If Tcapture = 0 then new values are not appended
to the data_capture array.


if self.Tcapture > <NUM_LIT:0>:<EOL><INDENT>self.DSP_tic.append(time.time()-self.start_time)<EOL><DEDENT>

Add new tic time to the DSP_tic list. Will not be called if
Tcapture = 0.


if self.Tcapture > <NUM_LIT:0>:<EOL><INDENT>self.DSP_toc.append(time.time()-self.start_time)<EOL><DEDENT>

Add new toc time to the DSP_toc list. Will not be called if
Tcapture = 0.


Tp = self.frame_length/float(self.fs)*<NUM_LIT:1000><EOL>print('<STR_LIT>'% (self.DSP_tic[<NUM_LIT:0>]*<NUM_LIT:1000>,))<EOL>print('<STR_LIT>' % Tp)<EOL>Tmp_mean = np.mean(np.diff(np.array(self.DSP_tic))[<NUM_LIT:1>:]*<NUM_LIT:1000>)<EOL>print('<STR_LIT>' % Tmp_mean)<EOL>Tprocess_mean = np.mean(np.array(self.DSP_toc)-np.array(self.DSP_tic))*<NUM_LIT:1000><EOL>print('<STR_LIT>' % Tprocess_mean)<EOL>

Display basic statistics of callback execution: ideal period
between callbacks, average measured period between callbacks,
and average time spent in the callback.


<EOL>k_min_idx = np.nonzero(np.ravel(np.array(self.DSP_tic)*<NUM_LIT:1000> < start_ms))[<NUM_LIT:0>]<EOL>if len(k_min_idx) < <NUM_LIT:1>:<EOL><INDENT>k_min = <NUM_LIT:0><EOL><DEDENT>else:<EOL><INDENT>k_min = k_min_idx[-<NUM_LIT:1>]<EOL><DEDENT>k_max_idx = np.nonzero(np.ravel(np.array(self.DSP_tic)*<NUM_LIT:1000> > stop_ms))[<NUM_LIT:0>]<EOL>if len(k_min_idx) < <NUM_LIT:1>:<EOL><INDENT>k_max= len(self.DSP_tic)<EOL><DEDENT>else:<EOL><INDENT>k_max = k_max_idx[<NUM_LIT:0>]<EOL><DEDENT>for k in range(k_min,k_max):<EOL><INDENT>if k == <NUM_LIT:0>:<EOL><INDENT>plt.plot([<NUM_LIT:0>,self.DSP_tic[k]*<NUM_LIT:1000>,self.DSP_tic[k]*<NUM_LIT:1000>,<EOL>self.DSP_toc[k]*<NUM_LIT:1000>,self.DSP_toc[k]*<NUM_LIT:1000>],<EOL>[<NUM_LIT:0>,<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:1>,<NUM_LIT:0>],'<STR_LIT:b>')<EOL><DEDENT>else:<EOL><INDENT>plt.plot([self.DSP_toc[k-<NUM_LIT:1>]*<NUM_LIT:1000>,self.DSP_tic[k]*<NUM_LIT:1000>,self.DSP_tic[k]*<NUM_LIT:1000>,<EOL>self.DSP_toc[k]*<NUM_LIT:1000>,self.DSP_toc[k]*<NUM_LIT:1000>],[<NUM_LIT:0>,<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:1>,<NUM_LIT:0>],'<STR_LIT:b>')<EOL><DEDENT><DEDENT>plt.plot([self.DSP_toc[k_max-<NUM_LIT:1>]*<NUM_LIT:1000>,stop_ms],[<NUM_LIT:0>,<NUM_LIT:0>],'<STR_LIT:b>')<EOL>plt.xlim([start_ms,stop_ms])<EOL>plt.title(r'<STR_LIT>')<EOL>plt.ylabel(r'<STR_LIT>')<EOL>plt.xlabel(r'<STR_LIT>')<EOL>plt.grid()<EOL>

Plot timing information of time spent in the callback. This is similar
to what a logic analyzer provides when probing an interrupt.

cb_active_plot( start_ms,stop_ms,line_color='b')


for i in range(<NUM_LIT:0>,self.frame_length*<NUM_LIT:2>):<EOL><INDENT>if i % <NUM_LIT:2>:<EOL><INDENT>self.right_in[(int)(i/<NUM_LIT:2>)] = in_data[i]<EOL><DEDENT>else:<EOL><INDENT>self.left_in[(int)(i/<NUM_LIT:2>)] = in_data[i]<EOL><DEDENT><DEDENT>return self.left_in, self.right_in<EOL>

Splits incoming packed stereo data into separate left and right channels
and returns an array of left samples and an array of right samples

Parameters
----------
in_data : input data from the streaming object in the callback function.

Returns
-------
left_in : array of incoming left channel samples
right_in : array of incoming right channel samples


for i in range(<NUM_LIT:0>,self.frame_length*<NUM_LIT:2>):<EOL><INDENT>if i % <NUM_LIT:2>:<EOL><INDENT>self.out[i] = right_out[(int)(i/<NUM_LIT:2>)]<EOL><DEDENT>else:<EOL><INDENT>self.out[i] = left_out[(int)(i/<NUM_LIT:2>)]<EOL><DEDENT><DEDENT>return self.out<EOL>

Packs separate left and right channel data into one array to output
and returns the output.

Parameters
----------
left_out : left channel array of samples going to output
right_out : right channel array of samples going to output

Returns
-------
out : packed left and right channel array of samples


def __init__(self,x,start_offset = <NUM_LIT:0>):

self.n_chan = x.ndim<EOL>if self.n_chan == <NUM_LIT:2>:<EOL><INDENT>if x.shape[<NUM_LIT:1>] != <NUM_LIT:2>:<EOL><INDENT>x = x.T<EOL><DEDENT><DEDENT>self.x = x<EOL>self.x_len = x.shape[<NUM_LIT:0>]<EOL>self.loop_pointer = start_offset<EOL>

Create a 1D or 2D array for audio looping


M = len(h)<EOL>N = <NUM_LIT:3>  <EOL>f = open(fname_out, '<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>' % M)<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>');<EOL>f.write('<STR_LIT>');<EOL>f.write('<STR_LIT>')<EOL>kk = <NUM_LIT:0>;<EOL>for k in range(M):<EOL><INDENT>if (kk < N - <NUM_LIT:1>) and (k < M - <NUM_LIT:1>):<EOL><INDENT>f.write('<STR_LIT>' % h[k])<EOL>kk += <NUM_LIT:1><EOL><DEDENT>elif (kk == N - <NUM_LIT:1>) & (k < M - <NUM_LIT:1>):<EOL><INDENT>f.write('<STR_LIT>' % h[k])<EOL>if k < M:<EOL><INDENT>f.write('<STR_LIT:U+0020>')<EOL>kk = <NUM_LIT:0><EOL><DEDENT><DEDENT>else:<EOL><INDENT>f.write('<STR_LIT>' % h[k])<EOL><DEDENT><DEDENT>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.close()<EOL>

Write FIR Filter Header Files

Mark Wickert February 2015