Instruction:
Write a Python 3 script for
finding and load the build backend

Response:
def _build_backend():
    """Find and load the build backend"""
    # Add in-tree backend directories to the front of sys.path.
    backend_path = os.environ.get('PEP517_BACKEND_PATH')
    if backend_path:
        extra_pathitems = backend_path.split(os.pathsep)
        sys.path[:0] = extra_pathitems

    ep = os.environ['PEP517_BUILD_BACKEND']
    mod_path, _, obj_path = ep.partition(':')
    try:
        obj = import_module(mod_path)
    except ImportError:
        raise BackendUnavailable(traceback.format_exc())

    if backend_path:
        if not any(
            contained_in(obj.__file__, path)
            for path in extra_pathitems
        ):
            raise BackendInvalid("Backend was not loaded from backend-path")

    if obj_path:
        for path_part in obj_path.split('.'):
            obj = getattr(obj, path_part)
    return obj


Instruction:
Write a Python 3 function that can
invoke the mandatory build_sdist hook.

Response:
def build_sdist(sdist_directory, config_settings):
    """Invoke the mandatory build_sdist hook."""
    backend = _build_backend()
    try:
        return backend.build_sdist(sdist_directory, config_settings)
    except getattr(backend, 'UnsupportedOperation', _DummyException):
        raise GotUnsupportedOperation(traceback.format_exc())


Instruction:
Create a Python 3 function to
print errors. Stop travis-ci from leaking api keys :param e: The error :return: None

Response:
def log_error(self, e):
        """
        Print errors. Stop travis-ci from leaking api keys

        :param e: The error
        :return: None
        """

        if not environ.get('CI'):
            self.log_function(e)
            if hasattr(e, 'response') and hasattr(e.response, 'text'):
                self.log_function(e.response.text)


Instruction:
Write a Python 3 script for
sleeping between requests but don t force asynchronous code to wait

Response:
def _sleep(self, seconds):
        """
        Sleep between requests, but don't force asynchronous code to wait

        :param seconds: The number of seconds to sleep
        :return: None
        """
        for _ in range(int(seconds)):
            if not self.force_stop:
                sleep(1)


Instruction:
How would you explain what the following Python 3 function does
def get(self, *args, **kwargs):

        """
        An interface for get requests that handles errors more gracefully to
        prevent data loss
        """

        try:
            req_func = self.session.get if self.session else requests.get
            req = req_func(*args, **kwargs)
            req.raise_for_status()
            self.failed_last = False
            return req

        except requests.exceptions.RequestException as e:
            self.log_error(e)
            for i in range(1, self.num_retries):
                sleep_time = self.retry_rate * i
                self.log_function("Retrying in %s seconds" % sleep_time)
                self._sleep(sleep_time)
                try:
                    req = requests.get(*args, **kwargs)
                    req.raise_for_status()
                    self.log_function("New request successful")
                    return req
                except requests.exceptions.RequestException:
                    self.log_function("New request failed")

            # Allows for the api to ignore one potentially bad request
            if not self.failed_last:
                self.failed_last = True
                raise ApiError(e)
            else:
                raise FatalApiError(e)

Response:
Returns a new instance of the class
            with the specified parameters.


Instruction:
How would you code a function in Python 3 to
return the node and edge info for a given node and edge.

Response:
def node_edge(self, node, edge, fields=None, params=None):

        """

        :param node:
        :param edge:
        :param fields:
        :param params:
        :return:
        """
        if fields:
            fields = ",".join(fields)

        parameters = {"fields": fields,
                      "access_token": self.key}
        parameters = self.merge_params(parameters, params)

        return self.api_call('%s/%s' % (node, edge), parameters)


Instruction:
Here you have a function in Python 3, explain what it does
def post(self, post_id, fields=None, **params):

        """

        :param post_id:
        :param fields:
        :param params:
        :return:
        """
        if fields:
            fields = ",".join(fields)

        parameters = {"fields": fields,
                      "access_token": self.key}
        parameters = self.merge_params(parameters, params)

        return self.api_call('%s' % post_id, parameters)

Response: Send a POST request to the server.


Instruction:
Make a summary of the following Python 3 code
def page_posts(self, page_id, after='', post_type="posts",
                   include_hidden=False, fields=None, **params):

        """

        :param page_id:
        :param after:
        :param post_type: Can be 'posts', 'feed', 'tagged', 'promotable_posts'
        :param include_hidden:
        :param fields:
        :param params:
        :return:
        """
        if fields:
            fields = ",".join(fields)

        parameters = {"access_token": self.key,
                      "after": after,
                      "fields": fields,
                      "include_hidden": include_hidden}
        parameters = self.merge_params(parameters, params)

        return self.api_call('%s/%s' % (page_id, post_type), parameters)

Response: Returns a list of all the posts in a page.


Instruction:
Implement a function in Python 3 to
post comments to a specific post.

Response:
def post_comments(self, post_id, after='', order="chronological",
                      filter="stream", fields=None, **params):

        """

        :param post_id:
        :param after:
        :param order: Can be 'ranked', 'chronological', 'reverse_chronological'
        :param filter: Can be 'stream', 'toplevel'
        :param fields: Can be 'id', 'application', 'attachment', 'can_comment',
        'can_remove', 'can_hide', 'can_like', 'can_reply_privately', 'comments',
        'comment_count', 'created_time', 'from', 'likes', 'like_count',
        'live_broadcast_timestamp', 'message', 'message_tags', 'object',
        'parent', 'private_reply_conversation', 'user_likes'
        :param params:
        :return:
        """
        if fields:
            fields = ",".join(fields)

        parameters = {"access_token": self.key,
                      "after": after,
                      "order": order,
                      "fields": fields,
                      "filter": filter}
        parameters = self.merge_params(parameters, params)

        return self.api_call('%s/comments' % post_id, parameters)


Instruction:
Here you have a function in Python 3, explain what it does
def flatten(dictionary, parent_key=False, separator='.'):

    items = []
    for key, value in dictionary.items():
        new_key = str(parent_key) + separator + key if parent_key else key
        if isinstance(value, collections.MutableMapping):
            items.extend(flatten(value, new_key, separator).items())
        elif isinstance(value, list):
            for k, v in enumerate(value):
                items.extend(flatten({str(k): v}, new_key).items())
        else:
            items.append((new_key, value))
    return dict(items)

Response: Converts a nested dictionary into a flattened dictionary.


Instruction:
How would you explain what the following Python 3 function does
def fill_gaps(list_dicts):

    field_names = []  # != set bc. preserving order is better for output
    for datum in list_dicts:
        for key in datum.keys():
            if key not in field_names:
                field_names.append(key)
    for datum in list_dicts:
        for key in field_names:
            if key not in datum:
                datum[key] = ''
    return list(field_names), list_dicts

Response: Fill gaps in a list of dictionaries. Add empty keys to dictionaries in that don t contain other entries


Instruction:
Can you tell what is the following Python 3 function doing
def to_csv(data, field_names=None, filename='data.csv',
           overwrite=True,
           write_headers=True, append=False, flat=True,
           primary_fields=None, sort_fields=True):
    """
    DEPRECATED    Write a list of dicts to a csv file

    :param data: List of dicts
    :param field_names: The list column names
    :param filename: The name of the file
    :param overwrite: Overwrite the file if exists
    :param write_headers: Write the headers to the csv file
    :param append: Write new rows if the file exists
    :param flat: Flatten the dictionary before saving
    :param primary_fields: The first columns of the csv file
    :param sort_fields: Sort the field names alphabetically
    :return: None
    """

    # Don't overwrite if not specified
    if not overwrite and path.isfile(filename):
        raise FileExistsError('The file already exists')

    # Replace file if append not specified
    write_type = 'w' if not append else 'a'

    # Flatten if flat is specified, or there are no predefined field names
    if flat or not field_names:
        data = [flatten(datum) for datum in data]

    # Fill in gaps between dicts with empty string
    if not field_names:
        field_names, data = fill_gaps(data)

    # Sort fields if specified
    if sort_fields:
        field_names.sort()

    # If there are primary fields, move the field names to the front and sort
    #  based on first field
    if primary_fields:
        for key in primary_fields[::-1]:
            field_names.insert(0, field_names.pop(field_names.index(key)))

        data = sorted(data, key=lambda k: k[field_names[0]], reverse=True)

    # Write the file
    with open(filename, write_type, encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=field_names, lineterminator='\n')
        if not append or write_headers:
            writer.writeheader()

        # Write rows containing fields in field names
        for datum in data:
            for key in list(datum.keys()):
                if key not in field_names:
                    del datum[key]
                elif type(datum[key]) is str:
                    datum[key] = datum[key].strip()

                datum[key] = str(datum[key])

            writer.writerow(datum)

Response: Write a list of dicts to a CSV file.


Instruction:
Can you write a function in Python 3 where it
writes an object to a json file

Response:
def to_json(data, filename='data.json', indent=4):
    """
    Write an object to a json file

    :param data: The object
    :param filename: The name of the file
    :param indent: The indentation of the file
    :return: None
    """

    with open(filename, 'w') as f:
        f.write(json.dumps(data, indent=indent))


Instruction:
Write a Python 3 function that can
download and save a file at path Taxonomy

Response:
def save_file(filename, source, folder="Downloads"):
    """
    Download and save a file at path

    :param filename: The name of the file
    :param source: The location of the resource online
    :param folder: The directory the file will be saved in
    :return: None
    """

    r = requests.get(source, stream=True)
    if r.status_code == 200:
        if not path.isdir(folder):
            makedirs(folder, exist_ok=True)
        with open("%s/%s" % (folder, filename), 'wb') as f:
            for chunk in r:
                f.write(chunk)


Instruction:
Can you write a function in Python 3 where it
converts frames from a tar file to a single video file.

Response:
def convert_frames_to_video(tar_file_path, output_path="output.mp4", framerate=60, overwrite=False):
    """
    Try to convert a tar file containing a sequence of frames saved by the
    meshcat viewer into a single video file.

    This relies on having `ffmpeg` installed on your system.
    """
    output_path = os.path.abspath(output_path)
    if os.path.isfile(output_path) and not overwrite:
        raise ValueError("The output path {:s} already exists. To overwrite that file, you can pass overwrite=True to this function.".format(output_path))
    with tempfile.TemporaryDirectory() as tmp_dir:
        with tarfile.open(tar_file_path) as tar:
            tar.extractall(tmp_dir)
        args = ["ffmpeg",
                "-r", str(framerate),
                "-i", r"%07d.png",
                "-vcodec", "libx264",
                "-preset", "slow",
                "-crf", "18"]
        if overwrite:
            args.append("-y")
        args.append(output_path)
        try:
            subprocess.check_call(args, cwd=tmp_dir)
        except subprocess.CalledProcessError as e:
            print("""
Could not call `ffmpeg` to convert your frames into a video.
If you want to convert the frames manually, you can extract the
.tar archive into a directory, cd to that directory, and run:
ffmpeg -r 60 -i %07d.png \\\n\t -vcodec libx264 \\\n\t -preset slow \\\n\t -crf 18 \\\n\t output.mp4
                """)
            raise
    print("Saved output as {:s}".format(output_path))
    return output_path


Instruction:
Can you tell what is the following Python 3 function doing
def toJSON(self):
        return {"id": self.id,
                "compile": self.compile,
                "position": self.position,
                "version": self.version}

Response: Get a json dict of the attributes of this object.


Instruction:
How would you explain what the following Python 3 function does
def _fix_docs(this_abc, child_class):
        # After python 3.5, this is basically handled automatically
        if sys.version_info >= (3, 5):
            return child_class

        if not issubclass(child_class, this_abc):
            raise KappaError('Cannot fix docs of class that is not decendent.')

        # This method is modified from solution given in
        # https://stackoverflow.com/a/8101598/8863865
        for name, child_func in vars(child_class).items():
            if callable(child_func) and not child_func.__doc__:
                if name in this_abc.__abstractmethods__:
                    parent_func = getattr(this_abc, name)
                    child_func.__doc__ = parent_func.__doc__
        return child_class

Response: Fix the documentation of the api methods.


Instruction:
Can you tell what is the following Python 3 function doing
def add_model_string(self, model_str, position=1, file_id=None):
        if file_id is None:
            file_id = self.make_unique_id('inlined_input')
        ret_data = self.file_create(File.from_string(model_str, position,
                                                     file_id))
        return ret_data

Response: Add a kappa model given in a string to the project.


Instruction:
How would you explain what the following Python 3 function does
def add_model_file(self, model_fpath, position=1, file_id=None):
        if file_id is None:
            file_id = self.make_unique_id('file_input')
        ret_data = self.file_create(File.from_file(model_fpath, position,
                                                   file_id))
        return ret_data

Response: Add a kappa model from a file at given path to the project.


Instruction:
Here you have a function in Python 3, explain what it does
def set_default_sim_param(self, *args, **kwargs):
        if len(args) is 1 and isinstance(args[0], SimulationParameter):
            self.__default_param = args[0]
        else:
            self.__default_param = SimulationParameter(*args, **kwargs)
        return

Response: Set the default simulation parameter.


Instruction:
Can you generate the documentation for the following Python 3 function
def get_is_sim_running(self):
        sim_info = self.simulation_info()
        try:
            progress_info = sim_info['simulation_info_progress']
            ret = progress_info['simulation_progress_is_running']
        except KeyError:  # Simulation has not been created.
            ret = False
        return ret

Response: Check if the current simulation is running.


Instruction:
Can you implement a function in Python 3 that
blocks until the simulation is done or timeout seconds exceeded.

Response:
def wait_for_simulation_stop(self, timeout=None):
        """Block until the simulation is done or timeout seconds exceeded.

        If the simulation stops before timeout, siminfo is returned.
        """
        start = datetime.now()
        while self.get_is_sim_running():
            sleep(0.5)
            if timeout is not None:
                if (datetime.now() - start).seconds >= timeout:
                    ret = None
                    break
        else:
            ret = self.simulation_info()
        return ret


Instruction:
Make a summary of the following Python 3 code
def available_devices():
    devices = {}
    pA = pyaudio.PyAudio()
    device_string = str()
    for k in range(pA.get_device_count()):
        dev = pA.get_device_info_by_index(k)
        devices[k] = {'name': dev['name'], 'inputs': dev['maxInputChannels'], 'outputs': dev['maxOutputChannels']}
        device_string += 'Index %d device name = %s, inputs = %d, outputs = %d\n' % \
                        (k,dev['name'],dev['maxInputChannels'],dev['maxOutputChannels'])
    logger.debug(device_string)
    return devices

Response:
Display available audio devices along with their
    port indices.


Instruction:
Create a Python 3 function to
check the input and output devices to see if they are valid

Response:
def in_out_check(self):
        """
        Checks the input and output to see if they are valid

        """
        devices = available_devices()
        if not self.in_idx in devices:
            raise OSError("Input device is unavailable")
        in_check = devices[self.in_idx]
        if not self.out_idx in devices:
            raise OSError("Output device is unavailable")
        out_check = devices[self.out_idx]
        if((in_check['inputs'] == 0) and (out_check['outputs']==0)):
            raise StandardError('Invalid input and output devices')
        elif(in_check['inputs'] == 0):
            raise ValueError('Selected input device has no inputs')
        elif(out_check['outputs'] == 0):
            raise ValueError('Selected output device has no outputs')
        return True


Instruction:
Can you tell what is the following Python 3 function doing
def interactive_stream(self,Tsec = 2, numChan = 1):
        self.Tsec = Tsec
        self.numChan = numChan
        self.interactiveFG = 1
        self.play = interactive(self.interaction,Stream = ToggleButtons(
                                options=['Start Streaming', 'Stop Streaming'],
                                description = ' ',
                                value = 'Stop Streaming') )
        display(self.play)

Response: A convenience method for interacting with the audio stream.


Instruction:
Create a Python 3 function to
stream audio in a thread using callback.

Response:
def thread_stream(self,Tsec = 2,numChan = 1):
        """
        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_thread(time,channel):
            self.stream(Tsec=time,numChan = channel)

        # Thread the streaming function
        t = Thread(target=stream_thread, args=(Tsec,numChan,))

        # Start the stream
        t.start()


Instruction:
Explain what the following Python 3 code does
def stream(self,Tsec = 2,numChan = 1):
        self.Tsec = Tsec
        self.numChan = numChan
        self.N_samples = int(self.fs*Tsec)
        self.data_capture = []
        self.data_capture_left = []
        self.data_capture_right = []
        self.capture_sample_count = 0
        self.DSP_tic = []
        self.DSP_toc = []
        self.start_time = time.time()
        self.stop_stream = False
        # open stream using callback (3)
        stream = self.p.open(format=pyaudio.paInt16,
                             channels=numChan,
                             rate=self.fs,
                             input=True,
                             output=True,
                             input_device_index = self.in_idx,
                             output_device_index = self.out_idx,
                             frames_per_buffer = self.frame_length,
                             stream_callback=self.stream_callback)

        # start the stream (4)
        stream.start_stream()

        # infinite mode
        if(Tsec == 0):
            while stream.is_active():
                if self.stop_stream:
                    stream.stop_stream()
                time.sleep(self.sleep_time)
        else:
        # wait for stream to finish (5)
            while stream.is_active():
                if self.capture_sample_count >= self.N_samples:
                    stream.stop_stream()
                if self.stop_stream:
                    stream.stop_stream()
                time.sleep(self.sleep_time)

        # stop stream (6)
        stream.stop_stream()
        stream.close()

        # close PyAudio (7)
        self.p.terminate()
        self.stream_data = True
        # print('Audio input/output streaming session complete!')

        if(self.interactiveFG):
            # Move radio button back to 'Stop Streaming'
            self.play.children[0].value = 'Stop Streaming'
        else:
            if(self.print_when_done == 1):
                print('Completed')

Response: Open audio stream using callback.


Instruction:
Write a Python 3 script for
adding new samples to the data_capture array and increment the sample counter.

Response:
def DSP_capture_add_samples(self,new_data):
        """
        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 += len(new_data)
        if self.Tcapture > 0:
            self.data_capture = np.hstack((self.data_capture,new_data))
            if (self.Tcapture > 0) and (len(self.data_capture) > self.Ncapture):
                self.data_capture = self.data_capture[-self.Ncapture:]


Instruction:
Can you generate the documentation for the following Python 3 function
def DSP_capture_add_samples_stereo(self,new_data_left,new_data_right):
        self.capture_sample_count = self.capture_sample_count + len(new_data_left) + len(new_data_right)
        if self.Tcapture > 0:
            self.data_capture_left = np.hstack((self.data_capture_left,new_data_left))
            self.data_capture_right = np.hstack((self.data_capture_right,new_data_right))
            if (len(self.data_capture_left) > self.Ncapture):
                self.data_capture_left = self.data_capture_left[-self.Ncapture:]
            if (len(self.data_capture_right) > self.Ncapture):
                self.data_capture_right = self.data_capture_right[-self.Ncapture:]

Response: Add new samples to the data_capture array.


Instruction:
Can you implement a function in Python 3 that
adds new tic time to the list of tic times. Will not be called if Tcapture is 0.

Response:
def DSP_callback_tic(self):
        """
        Add new tic time to the DSP_tic list. Will not be called if
        Tcapture = 0.

        """
        if self.Tcapture > 0:
            self.DSP_tic.append(time.time()-self.start_time)


Instruction:
Create a Python 3 function for
adding new toc time to the DSP_toc list. Will not be called if Tcapture is 0.

Response:
def DSP_callback_toc(self):
        """
        Add new toc time to the DSP_toc list. Will not be called if
        Tcapture = 0.

        """
        if self.Tcapture > 0:
            self.DSP_toc.append(time.time()-self.start_time)


Instruction:
Make a summary of the following Python 3 code
def stream_stats(self):
        Tp = self.frame_length/float(self.fs)*1000
        print('Delay (latency) in Entering the Callback the First Time = %6.2f (ms)' \
              % (self.DSP_tic[0]*1000,))
        print('Ideal Callback period = %1.2f (ms)' % Tp)
        Tmp_mean = np.mean(np.diff(np.array(self.DSP_tic))[1:]*1000)
        print('Average Callback Period = %1.2f (ms)' % Tmp_mean)
        Tprocess_mean = np.mean(np.array(self.DSP_toc)-np.array(self.DSP_tic))*1000
        print('Average Callback process time = %1.2f (ms)' % Tprocess_mean)

Response: Display basic statistics of the callback execution


Instruction:
Can you write a function in Python 3 where it
plots 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')

Response:
def cb_active_plot(self,start_ms,stop_ms,line_color='b'):
        """
        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')

        """
        # Find bounding k values that contain the [start_ms,stop_ms]
        k_min_idx = np.nonzero(np.ravel(np.array(self.DSP_tic)*1000 < start_ms))[0]
        if len(k_min_idx) < 1:
            k_min = 0
        else:
            k_min = k_min_idx[-1]
        k_max_idx = np.nonzero(np.ravel(np.array(self.DSP_tic)*1000 > stop_ms))[0]
        if len(k_min_idx) < 1:
            k_max= len(self.DSP_tic)
        else:
            k_max = k_max_idx[0]
        for k in range(k_min,k_max):
            if k == 0:
                plt.plot([0,self.DSP_tic[k]*1000,self.DSP_tic[k]*1000,
                         self.DSP_toc[k]*1000,self.DSP_toc[k]*1000],
                        [0,0,1,1,0],'b')
            else:
                plt.plot([self.DSP_toc[k-1]*1000,self.DSP_tic[k]*1000,self.DSP_tic[k]*1000,
                          self.DSP_toc[k]*1000,self.DSP_toc[k]*1000],[0,0,1,1,0],'b')
        plt.plot([self.DSP_toc[k_max-1]*1000,stop_ms],[0,0],'b')

        plt.xlim([start_ms,stop_ms])
        plt.title(r'Time Spent in the callback')
        plt.ylabel(r'Timing')
        plt.xlabel(r'Time (ms)')
        plt.grid();


Instruction:
How would you explain what the following Python 3 function does
def get_LR(self,in_data):
        for i in range(0,self.frame_length*2):
            if i % 2:
                self.right_in[(int)(i/2)] = in_data[i]
            else:
                self.left_in[(int)(i/2)] = in_data[i]
        return self.left_in, self.right_in

Response: Returns a list of left and right samples and the list of samples with the highest priority.


Instruction:
Can you create a Python 3 function that
packs separate left and right channel data into one array to output and returns the output.

Response:
def pack_LR(self,left_out,right_out):
        """
        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
        """
        for i in range(0,self.frame_length*2):
            if i % 2:
                self.out[i] = right_out[(int)(i/2)]
            else:
                self.out[i] = left_out[(int)(i/2)]
        return self.out


Instruction:
How would you code a function in Python 3 to
design an IIR lowpass filter using scipy.signal.iirdesign. The filter order is determined based on f_pass Hz, f_stop Hz, and the desired stopband attenuation d_stop in dB, all relative to a sampling rate of fs Hz. Parameters ---------- f_pass : Passband critical frequency in Hz f_stop : Stopband critical frequency in Hz Ripple_pass : Filter gain in dB at f_pass Atten_stop : Filter attenuation in dB at f_stop fs : Sampling rate in Hz ftype : Analog prototype from 'butter' 'cheby1', 'cheby2', 'ellip', and 'bessel' Returns ------- b : ndarray of the numerator coefficients a : ndarray of the denominator coefficients sos : 2D ndarray of second-order section coefficients Notes ----- Additionally a text string telling the user the filter order is written to the console, e.g., IIR cheby1 order = 8. Examples -------- >>> fs = 48000 >>> f_pass = 5000 >>> f_stop = 8000 >>> b_but,a_but,sos_but = IIR_lpf(f_pass,f_stop,0.5,60,fs,'butter') >>> b_cheb1,a_cheb1,sos_cheb1 = IIR_lpf(f_pass,f_stop,0.5,60,fs,'cheby1') >>> b_cheb2,a_cheb2,sos_cheb2 = IIR_lpf(f_pass,f_stop,0.5,60,fs,'cheby2') >>> b_elli,a_elli,sos_elli = IIR_lpf(f_pass,f_stop,0.5,60,fs,'ellip') Mark Wickert October 2016

Response:
def IIR_lpf(f_pass, f_stop, Ripple_pass, Atten_stop,
            fs = 1.00, ftype = 'butter'):
    """
    Design an IIR lowpass filter using scipy.signal.iirdesign.
    The filter order is determined based on
    f_pass Hz, f_stop Hz, and the desired stopband attenuation
    d_stop in dB, all relative to a sampling rate of fs Hz.

    Parameters
    ----------
    f_pass : Passband critical frequency in Hz
    f_stop : Stopband critical frequency in Hz
    Ripple_pass : Filter gain in dB at f_pass
    Atten_stop : Filter attenuation in dB at f_stop
    fs : Sampling rate in Hz
    ftype : Analog prototype from 'butter' 'cheby1', 'cheby2',
            'ellip', and 'bessel'

    Returns
    -------
    b : ndarray of the numerator coefficients
    a : ndarray of the denominator coefficients
    sos : 2D ndarray of second-order section coefficients

    Notes
    -----
    Additionally a text string telling the user the filter order is
    written to the console, e.g., IIR cheby1 order = 8.

    Examples
    --------
    >>> fs = 48000
    >>> f_pass = 5000
    >>> f_stop = 8000
    >>> b_but,a_but,sos_but = IIR_lpf(f_pass,f_stop,0.5,60,fs,'butter')
    >>> b_cheb1,a_cheb1,sos_cheb1 = IIR_lpf(f_pass,f_stop,0.5,60,fs,'cheby1')
    >>> b_cheb2,a_cheb2,sos_cheb2 = IIR_lpf(f_pass,f_stop,0.5,60,fs,'cheby2')
    >>> b_elli,a_elli,sos_elli = IIR_lpf(f_pass,f_stop,0.5,60,fs,'ellip')

    Mark Wickert October 2016
    """

    b,a = signal.iirdesign(2*float(f_pass)/fs, 2*float(f_stop)/fs,
                           Ripple_pass, Atten_stop,
                           ftype = ftype, output='ba')
    sos = signal.iirdesign(2*float(f_pass)/fs, 2*float(f_stop)/fs,
                           Ripple_pass, Atten_stop,
                           ftype = ftype, output='sos')
    tag = 'IIR ' + ftype + ' order'
    print('%s = %d.' % (tag,len(a)-1))
    return b, a, sos


Instruction:
Given the following Python 3 function, write the documentation
def IIR_bsf(f_pass1, f_stop1, f_stop2, f_pass2, Ripple_pass, Atten_stop,
            fs = 1.00, ftype = 'butter'):
    """
    Design an IIR bandstop filter using scipy.signal.iirdesign.
    The filter order is determined based on
    f_pass Hz, f_stop Hz, and the desired stopband attenuation
    d_stop in dB, all relative to a sampling rate of fs Hz.

    Mark Wickert October 2016
    """

    b,a = signal.iirdesign([2*float(f_pass1)/fs, 2*float(f_pass2)/fs],
                           [2*float(f_stop1)/fs, 2*float(f_stop2)/fs],
                           Ripple_pass, Atten_stop,
                           ftype = ftype, output='ba')
    sos = signal.iirdesign([2*float(f_pass1)/fs, 2*float(f_pass2)/fs],
                           [2*float(f_stop1)/fs, 2*float(f_stop2)/fs],
                           Ripple_pass, Atten_stop,
                           ftype =ftype, output='sos')
    tag = 'IIR ' + ftype + ' order'
    print('%s = %d.' % (tag,len(a)-1))
    return b, a, sos

Response: This function is a wrapper for the numpy. array. sort function of the IIR bandstop filter


Instruction:
Can you tell what is the following Python 3 function doing
def freqz_resp_list(b,a=np.array([1]),mode = 'dB',fs=1.0,Npts = 1024,fsize=(6,4)):
    if type(b) == list:
        # We have a list of filters
        N_filt = len(b)
    f = np.arange(0,Npts)/(2.0*Npts)
    for n in range(N_filt):
        w,H = signal.freqz(b[n],a[n],2*np.pi*f)
        if n == 0:
            plt.figure(figsize=fsize)
        if mode.lower() == 'db':
            plt.plot(f*fs,20*np.log10(np.abs(H)))
            if n == N_filt-1:
                plt.xlabel('Frequency (Hz)')
                plt.ylabel('Gain (dB)')
                plt.title('Frequency Response - Magnitude')

        elif mode.lower() == 'phase':
            plt.plot(f*fs,np.angle(H))
            if n == N_filt-1:
                plt.xlabel('Frequency (Hz)')
                plt.ylabel('Phase (rad)')
                plt.title('Frequency Response - Phase')

        elif (mode.lower() == 'groupdelay_s') or (mode.lower() == 'groupdelay_t'):
            """
            Notes
            -----

            Since this calculation involves finding the derivative of the
            phase response, care must be taken at phase wrapping points
            and when the phase jumps by +/-pi, which occurs when the
            amplitude response changes sign. Since the amplitude response
            is zero when the sign changes, the jumps do not alter the group
            delay results.
            """
            theta = np.unwrap(np.angle(H))
            # Since theta for an FIR filter is likely to have many pi phase
            # jumps too, we unwrap a second time 2*theta and divide by 2
            theta2 = np.unwrap(2*theta)/2.
            theta_dif = np.diff(theta2)
            f_diff = np.diff(f)
            Tg = -np.diff(theta2)/np.diff(w)
            # For gain almost zero set groupdelay = 0
            idx = np.nonzero(np.ravel(20*np.log10(H[:-1]) < -400))[0]
            Tg[idx] = np.zeros(len(idx))
            max_Tg = np.max(Tg)
            #print(max_Tg)
            if mode.lower() == 'groupdelay_t':
                max_Tg /= fs
                plt.plot(f[:-1]*fs,Tg/fs)
                plt.ylim([0,1.2*max_Tg])
            else:
                plt.plot(f[:-1]*fs,Tg)
                plt.ylim([0,1.2*max_Tg])
            if n == N_filt-1:
                plt.xlabel('Frequency (Hz)')
                if mode.lower() == 'groupdelay_t':
                    plt.ylabel('Group Delay (s)')
                else:
                    plt.ylabel('Group Delay (samples)')
                plt.title('Frequency Response - Group Delay')
        else:
            s1 = 'Error, mode must be "dB", "phase, '
            s2 = '"groupdelay_s", or "groupdelay_t"'
            print(s1 + s2)

Response: A list of frequency response methods that can be used to display the frequency response of a single object.


Instruction:
Explain what the following Python 3 code does
def freqz_cas(sos,w):
    Ns,Mcol = sos.shape
    w,Hcas = signal.freqz(sos[0,:3],sos[0,3:],w)
    for k in range(1,Ns):
        w,Htemp = signal.freqz(sos[k,:3],sos[k,3:],w)
        Hcas *= Htemp
    return w, Hcas

Response: Returns the frequency of the next non - empty sequence in a sequence.


Instruction:
Here you have a function in Python 3, explain what it does
def unique_cpx_roots(rlist,tol = 0.001):
    uniq = [rlist[0]]
    mult = [1]
    for k in range(1,len(rlist)):
        N_uniq = len(uniq)
        for m in range(N_uniq):
            if abs(rlist[k]-uniq[m]) <= tol:
                mult[m] += 1
                uniq[m] = (uniq[m]*(mult[m]-1) + rlist[k])/float(mult[m])
                break
        uniq = np.hstack((uniq,rlist[k]))
        mult = np.hstack((mult,[1]))
    return np.array(uniq), np.array(mult)

Response: Returns a list of unique cpx_root entries in the given list.


Instruction:
Write a Python 3 script for
creating a z - plane plot from a single sos object.

Response:
def sos_zplane(sos,auto_scale=True,size=2,tol = 0.001):
    """
    Create an z-plane pole-zero plot.

    Create an z-plane pole-zero plot using the numerator
    and denominator z-domain system function coefficient
    ndarrays b and a respectively. Assume descending powers of z.

    Parameters
    ----------
    sos : ndarray of the sos coefficients
    auto_scale : bool (default True)
    size : plot radius maximum when scale = False

    Returns
    -------
    (M,N) : tuple of zero and pole counts + plot window

    Notes
    -----
    This function tries to identify repeated poles and zeros and will
    place the multiplicity number above and to the right of the pole or zero.
    The difficulty is setting the tolerance for this detection. Currently it
    is set at 1e-3 via the function signal.unique_roots.

    Examples
    --------
    >>> # Here the plot is generated using auto_scale
    >>> sos_zplane(sos)
    >>> # Here the plot is generated using manual scaling
    >>> sos_zplane(sos,False,1.5)
    """
    Ns,Mcol = sos.shape
    # Extract roots from sos num and den removing z = 0
    # roots due to first-order sections
    N_roots = []
    for k in range(Ns):
        N_roots_tmp = np.roots(sos[k,:3])
        if N_roots_tmp[1] == 0.:
            N_roots = np.hstack((N_roots,N_roots_tmp[0]))
        else:
            N_roots = np.hstack((N_roots,N_roots_tmp))
    D_roots = []
    for k in range(Ns):
        D_roots_tmp = np.roots(sos[k,3:])
        if D_roots_tmp[1] == 0.:
            D_roots = np.hstack((D_roots,D_roots_tmp[0]))
        else:
            D_roots = np.hstack((D_roots,D_roots_tmp))
    # Plot labels if multiplicity greater than 1
    x_scale = 1.5*size
    y_scale = 1.5*size
    x_off = 0.02
    y_off = 0.01
    M = len(N_roots)
    N = len(D_roots)
    if auto_scale:
        if M > 0 and N > 0:
            size = max(np.max(np.abs(N_roots)),np.max(np.abs(D_roots)))+.1
        elif M > 0:
            size = max(np.max(np.abs(N_roots)),1.0)+.1
        elif N > 0:
            size = max(1.0,np.max(np.abs(D_roots)))+.1
        else:
            size = 1.1
    plt.figure(figsize=(5,5))
    plt.axis('equal')
    r = np.linspace(0,2*np.pi,200)
    plt.plot(np.cos(r),np.sin(r),'r--')
    plt.plot([-size,size],[0,0],'k-.')
    plt.plot([0,0],[-size,size],'k-.')
    if M > 0:
        #N_roots = np.roots(b)
        N_uniq, N_mult=unique_cpx_roots(N_roots,tol=tol)
        plt.plot(np.real(N_uniq),np.imag(N_uniq),'ko',mfc='None',ms=8)
        idx_N_mult = np.nonzero(np.ravel(N_mult>1))[0]
        for k in range(len(idx_N_mult)):
            x_loc = np.real(N_uniq[idx_N_mult[k]]) + x_off*x_scale
            y_loc =np.imag(N_uniq[idx_N_mult[k]]) + y_off*y_scale
            plt.text(x_loc,y_loc,str(N_mult[idx_N_mult[k]]),
                     ha='center',va='bottom',fontsize=10)
    if N > 0:
        #D_roots = np.roots(a)
        D_uniq, D_mult=unique_cpx_roots(D_roots,tol=tol)
        plt.plot(np.real(D_uniq),np.imag(D_uniq),'kx',ms=8)
        idx_D_mult = np.nonzero(np.ravel(D_mult>1))[0]
        for k in range(len(idx_D_mult)):
            x_loc = np.real(D_uniq[idx_D_mult[k]]) + x_off*x_scale
            y_loc =np.imag(D_uniq[idx_D_mult[k]]) + y_off*y_scale
            plt.text(x_loc,y_loc,str(D_mult[idx_D_mult[k]]),
                     ha='center',va='bottom',fontsize=10)
    if M - N < 0:
        plt.plot(0.0,0.0,'bo',mfc='None',ms=8)
    elif M - N > 0:
        plt.plot(0.0,0.0,'kx',ms=8)
    if abs(M - N) > 1:
        plt.text(x_off*x_scale,y_off*y_scale,str(abs(M-N)),
                 ha='center',va='bottom',fontsize=10)
    plt.xlabel('Real Part')
    plt.ylabel('Imaginary Part')
    plt.title('Pole-Zero Plot')
    #plt.grid()
    plt.axis([-size,size,-size,size])
    return M,N


Instruction:
Here you have a function in Python 3, explain what it does
def firwin_bpf(N_taps, f1, f2, fs = 1.0, pass_zero=False):
    return signal.firwin(N_taps,2*(f1,f2)/fs,pass_zero=pass_zero)

Response: Returns a new FIR bandpass filter with the given frequency range


Instruction:
Can you generate the documentation for the following Python 3 function
def firwin_kaiser_lpf(f_pass, f_stop, d_stop, fs = 1.0, N_bump=0):
    wc = 2*np.pi*(f_pass + f_stop)/2/fs
    delta_w = 2*np.pi*(f_stop - f_pass)/fs
    # Find the filter order
    M = np.ceil((d_stop - 8)/(2.285*delta_w))
    # Adjust filter order up or down as needed
    M += N_bump
    N_taps = M + 1
    # Obtain the Kaiser window
    beta = signal.kaiser_beta(d_stop)
    w_k = signal.kaiser(N_taps,beta)
    n = np.arange(N_taps)
    b_k = wc/np.pi*np.sinc(wc/np.pi*(n-M/2)) * w_k
    b_k /= np.sum(b_k)
    print('Kaiser Win filter taps = %d.' % N_taps)
    return b_k

Response: This function returns a lowpass filter based on the sinc function kernel and a Kaiser Win filter.


Instruction:
Implement a Python 3 function for
designing an FIR bandpass filter using the sinc function and the Kaiser window.

Response:
def firwin_kaiser_bpf(f_stop1, f_pass1, f_pass2, f_stop2, d_stop,
                      fs = 1.0, N_bump=0):
    """
    Design an FIR bandpass filter using the sinc() kernel and
    a Kaiser window. The filter order is determined based on
    f_stop1 Hz, f_pass1 Hz, f_pass2 Hz, f_stop2 Hz, and the
    desired stopband attenuation d_stop in dB for both stopbands,
    all relative to a sampling rate of fs Hz.
    Note: the passband ripple cannot be set independent of the
    stopband attenuation.

    Mark Wickert October 2016
    """
    # Design BPF starting from simple LPF equivalent
    # The upper and lower stopbands are assumed to have
    # the same attenuation level. The LPF equivalent critical
    # frequencies:
    f_pass = (f_pass2 - f_pass1)/2
    f_stop = (f_stop2 - f_stop1)/2
    # Continue to design equivalent LPF
    wc = 2*np.pi*(f_pass + f_stop)/2/fs
    delta_w = 2*np.pi*(f_stop - f_pass)/fs
    # Find the filter order
    M = np.ceil((d_stop - 8)/(2.285*delta_w))
    # Adjust filter order up or down as needed
    M += N_bump
    N_taps = M + 1
    # Obtain the Kaiser window
    beta = signal.kaiser_beta(d_stop)
    w_k = signal.kaiser(N_taps,beta)
    n = np.arange(N_taps)
    b_k = wc/np.pi*np.sinc(wc/np.pi*(n-M/2)) * w_k
    b_k /= np.sum(b_k)
    # Transform LPF to BPF
    f0 = (f_pass2 + f_pass1)/2
    w0 = 2*np.pi*f0/fs
    n = np.arange(len(b_k))
    b_k_bp = 2*b_k*np.cos(w0*(n-M/2))
    print('Kaiser Win filter taps = %d.' % N_taps)
    return b_k_bp


Instruction:
Create a Python 3 function to
return a new order based on the given conditions.

Response:
def lowpass_order(f_pass, f_stop, dpass_dB, dstop_dB, fsamp = 1):
    """
    Optimal FIR (equal ripple) Lowpass Order Determination

    Text reference: Ifeachor, Digital Signal Processing a Practical Approach,
    second edition, Prentice Hall, 2002.
    Journal paper reference: Herriman et al., Practical Design Rules for Optimum
    Finite Imulse Response Digitl Filters, Bell Syst. Tech. J., vol 52, pp.
    769-799, July-Aug., 1973.IEEE, 1973.
    """
    dpass = 1 - 10**(-dpass_dB/20)
    dstop = 10**(-dstop_dB/20)
    Df = (f_stop - f_pass)/fsamp
    a1 = 5.309e-3
    a2 = 7.114e-2
    a3 = -4.761e-1
    a4 = -2.66e-3
    a5 = -5.941e-1
    a6 = -4.278e-1

    Dinf = np.log10(dstop)*(a1*np.log10(dpass)**2 + a2*np.log10(dpass) + a3) \
           + (a4*np.log10(dpass)**2 + a5*np.log10(dpass) + a6)
    f = 11.01217 + 0.51244*(np.log10(dpass) - np.log10(dstop))
    N = Dinf/Df - f*Df + 1
    ff = 2*np.array([0, f_pass, f_stop, fsamp/2])/fsamp
    aa = np.array([1, 1, 0, 0])
    wts = np.array([1.0, dpass/dstop])
    return int(N), ff, aa, wts


Instruction:
Make a summary of the following Python 3 code
def bandpass_order(f_stop1, f_pass1, f_pass2, f_stop2, dpass_dB, dstop_dB, fsamp = 1):
    dpass = 1 - 10**(-dpass_dB/20)
    dstop = 10**(-dstop_dB/20)
    Df1 = (f_pass1 - f_stop1)/fsamp
    Df2 = (f_stop2 - f_pass2)/fsamp
    b1 = 0.01201
    b2 = 0.09664
    b3 = -0.51325
    b4 = 0.00203
    b5 = -0.5705
    b6 = -0.44314

    Df = min(Df1, Df2)
    Cinf = np.log10(dstop)*(b1*np.log10(dpass)**2 + b2*np.log10(dpass) + b3) \
           + (b4*np.log10(dpass)**2 + b5*np.log10(dpass) + b6)
    g = -14.6*np.log10(dpass/dstop) - 16.9
    N = Cinf/Df + g*Df + 1
    ff = 2*np.array([0, f_stop1, f_pass1, f_pass2, f_stop2, fsamp/2])/fsamp
    aa = np.array([0, 0, 1, 1, 0, 0])
    wts = np.array([dpass/dstop, 1, dpass/dstop])
    return int(N), ff, aa, wts

Response: Returns a new structure for a bandpass order


Instruction:
Given the following Python 3 function, write the documentation
def fir_remez_lpf(f_pass, f_stop, d_pass, d_stop, fs = 1.0, N_bump=5):
    n, ff, aa, wts = lowpass_order(f_pass, f_stop, d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts,Hz=2)
    print('Remez filter taps = %d.' % N_taps)
    return b

Response: Returns a FIR lowpass filter with the given filter parameters.


Instruction:
Can you tell what is the following Python 3 function doing
def fir_remez_hpf(f_stop, f_pass, d_pass, d_stop, fs = 1.0, N_bump=5):
    # Transform HPF critical frequencies to lowpass equivalent
    f_pass_eq = fs/2. - f_pass
    f_stop_eq = fs/2. - f_stop
    # Design LPF equivalent
    n, ff, aa, wts = lowpass_order(f_pass_eq, f_stop_eq, d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts,Hz=2)
    # Transform LPF equivalent to HPF
    n = np.arange(len(b))
    b *= (-1)**n
    print('Remez filter taps = %d.' % N_taps)
    return b

Response: Design an FIR highpass filter using remez with order determined by the desired passband ripple diameter f_pass and stopband attenuation d_stop dB.


Instruction:
Given the following Python 3 function, write the documentation
def fir_remez_bpf(f_stop1, f_pass1, f_pass2, f_stop2, d_pass, d_stop,
                  fs = 1.0, N_bump=5):
    """
    Design an FIR bandpass filter using remez with order
    determination. The filter order is determined based on
    f_stop1 Hz, f_pass1 Hz, f_pass2 Hz, f_stop2 Hz, and the
    desired passband ripple d_pass dB and stopband attenuation
    d_stop dB all relative to a sampling rate of fs Hz.

    Mark Wickert October 2016, updated October 2018
    """
    n, ff, aa, wts = bandpass_order(f_stop1, f_pass1, f_pass2, f_stop2,
                                  d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts,Hz=2)
    print('Remez filter taps = %d.' % N_taps)
    return b

Response: Returns a FIR bandpass filter with the given order


Instruction:
Can you generate the documentation for the following Python 3 function
def CIC(M, K):

    if K == 1:
        b = np.ones(M)
    else:
        h = np.ones(M)
        b = h
        for i in range(1, K):
            b = signal.convolve(b, h)  # cascade by convolving impulse responses

    # Make filter have unity gain at DC
    return b / np.sum(b)

Response: A functional form implementation of a cascade of integrator comb ( CIC ).


Instruction:
Given the following Python 3 function, write the documentation
def ten_band_eq_filt(x,GdB,Q=3.5):
    fs = 44100.0 # Hz
    NB = len(GdB)
    if not NB == 10:
        raise ValueError("GdB length not equal to ten")
    Fc = 31.25*2**np.arange(NB)
    B = np.zeros((NB,3))
    A = np.zeros((NB,3))

    # Create matrix of cascade coefficients
    for k in range(NB):
        [b,a] = peaking(GdB[k],Fc[k],Q)
        B[k,:] = b
        A[k,:] = a
    # Pass signal x through the cascade of ten filters
    y = np.zeros(len(x))
    for k in range(NB):
        if k == 0:
            y = signal.lfilter(B[k,:],A[k,:],x)
        else:
            y = signal.lfilter(B[k,:],A[k,:],y)
    return y

Response: Filter the input signal x with a ten - band equalizer having octave gain values in ndarray GdB and the Q of each filter is set to 3. 5 kHz.


Instruction:
Implement a Python 3 function for
creating a frequency response magnitude plot in dB of a ten band equalizer.

Response:
def ten_band_eq_resp(GdB,Q=3.5):
    """
    Create a frequency response magnitude plot in dB of a ten band equalizer
    using a semilogplot (semilogx()) type plot

    Parameters
    ----------
    GdB : Gain vector for 10 peaking filters [G0,...,G9]
    Q : Quality factor for each peaking filter (default 3.5)

    Returns
    -------
    Nothing : two plots are created

    Examples
    --------
    >>> import matplotlib.pyplot as plt
    >>> from sk_dsp_comm import sigsys as ss
    >>> ss.ten_band_eq_resp([0,10.0,0,0,-1,0,5,0,-4,0])
    >>> plt.show()
    """
    fs = 44100.0 # Hz
    NB = len(GdB)
    if not NB == 10:
        raise ValueError("GdB length not equal to ten")
    Fc = 31.25*2**np.arange(NB)
    B = np.zeros((NB,3));
    A = np.zeros((NB,3));

    # Create matrix of cascade coefficients
    for k in range(NB):
        b,a = peaking(GdB[k],Fc[k],Q,fs)
        B[k,:] = b
        A[k,:] = a
    # Create the cascade frequency response
    F = np.logspace(1,np.log10(20e3),1000)
    H = np.ones(len(F))*np.complex(1.0,0.0)
    for k in range(NB):
       w,Htemp = signal.freqz(B[k,:],A[k,:],2*np.pi*F/fs)
       H *= Htemp
    plt.figure(figsize=(6,4))
    plt.subplot(211)
    plt.semilogx(F,20*np.log10(abs(H)))
    plt.axis([10, fs/2, -12, 12])
    plt.grid()
    plt.title('Ten-Band Equalizer Frequency Response')
    plt.xlabel('Frequency (Hz)')
    plt.ylabel('Gain (dB)')
    plt.subplot(212)
    plt.stem(np.arange(NB),GdB,'b','bs')
    #plt.bar(np.arange(NB)-.1,GdB,0.2)
    plt.axis([0, NB-1, -12, 12])
    plt.xlabel('Equalizer Band Number')
    plt.ylabel('Gain Set (dB)')
    plt.grid()


Instruction:
Make a summary of the following Python 3 code
def ex6_2(n):
    x = np.zeros(len(n))
    for k, nn in enumerate(n):
        if nn >= -2 and nn <= 5:
            x[k] = 8 - nn
    return x

Response:
Generate a triangle pulse as described in Example 6 - 2
    of Chapter 6.


Instruction:
How would you explain what the following Python 3 function does
def position_CD(Ka,out_type = 'fb_exact'):
    rs = 10/(2*np.pi)
    # Load b and a ndarrays with the coefficients
    if out_type.lower() == 'open_loop':
        b = np.array([Ka*4000*rs])
        a = np.array([1,1275,31250,0])
    elif out_type.lower() == 'fb_approx':
        b = np.array([3.2*Ka*rs])
        a = np.array([1, 25, 3.2*Ka*rs])
    elif out_type.lower() == 'fb_exact':
        b = np.array([4000*Ka*rs])
        a = np.array([1, 1250+25, 25*1250, 4000*Ka*rs])
    else:
        raise ValueError('out_type must be: open_loop, fb_approx, or fc_exact')
    return b, a

Response: Returns the CD sled position control case study of Chapter 18.


Instruction:
Can you write a function in Python 3 where it
cruises control with PI controller and hill disturbance. This function returns various system function configurations for a the cruise control Case Study example found in the supplementary article. The plant model is obtained by the linearizing the equations of motion and the controller contains a proportional and integral gain term set via the closed-loop parameters natuarl frequency wn (rad/s) and damping zeta. Parameters ---------- wn : closed-loop natural frequency in rad/s, nominally 0.1 zeta : closed-loop damping factor, nominally 1.0 T : vehicle time constant, nominally 10 s vcruise : cruise velocity set point, nominally 75 mph vmax : maximum vehicle velocity, nominally 120 mph tf_mode : 'H', 'HE', 'HVW', or 'HED' controls the system function returned by the function 'H' : closed-loop system function V(s)/R(s) 'HE' : closed-loop system function E(s)/R(s) 'HVW' : closed-loop system function V(s)/W(s) 'HED' : closed-loop system function E(s)/D(s), where D is the hill disturbance input Returns ------- b : numerator coefficient ndarray a : denominator coefficient ndarray Examples -------- >>> # return the closed-loop system function output/input velocity >>> b,a = cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='H') >>> # return the closed-loop system function loop error/hill disturbance >>> b,a = cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='HED')

Response:
def cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='H'):
    """
    Cruise control with PI controller and hill disturbance.

    This function returns various system function configurations
    for a the cruise control Case Study example found in
    the supplementary article. The plant model is obtained by the
    linearizing the equations of motion and the controller contains a
    proportional and integral gain term set via the closed-loop parameters
    natuarl frequency wn (rad/s) and damping zeta.

    Parameters
    ----------
    wn : closed-loop natural frequency in rad/s, nominally 0.1
    zeta : closed-loop damping factor, nominally 1.0
    T : vehicle time constant, nominally 10 s
    vcruise : cruise velocity set point, nominally 75 mph
    vmax : maximum vehicle velocity, nominally 120 mph
    tf_mode : 'H', 'HE', 'HVW', or 'HED' controls the system function returned by the function
    'H'   : closed-loop system function V(s)/R(s)
    'HE'  : closed-loop system function E(s)/R(s)
    'HVW' : closed-loop system function V(s)/W(s)
    'HED' : closed-loop system function E(s)/D(s), where D is the hill disturbance input

    Returns
    -------
    b : numerator coefficient ndarray
    a : denominator coefficient ndarray

    Examples
    --------
    >>> # return the closed-loop system function output/input velocity
    >>> b,a = cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='H')
    >>> # return the closed-loop system function loop error/hill disturbance
    >>> b,a = cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='HED')
    """
    tau = T/2.*vmax/vcruise
    g = 9.8
    g *= 3*60**2/5280. # m/s to mph conversion
    Kp = T*(2*zeta*wn-1/tau)/vmax
    Ki = T*wn**2./vmax
    K = Kp*vmax/T
    print('wn = ', np.sqrt(K/(Kp/Ki)))
    print('zeta = ', (K + 1/tau)/(2*wn))
    a = np.array([1, 2*zeta*wn, wn**2])
    if tf_mode == 'H':
        b = np.array([K, wn**2])
    elif tf_mode == 'HE':
        b = np.array([1, 2*zeta*wn-K, 0.])
    elif tf_mode == 'HVW':
        b = np.array([ 1, wn**2/K+1/tau, wn**2/(K*tau)])
        b *= Kp
    elif tf_mode == 'HED':
        b = np.array([g, 0])
    else:
        raise ValueError('tf_mode must be: H, HE, HVU, or HED')
    return b, a


Instruction:
Can you tell what is the following Python 3 function doing
def splane(b,a,auto_scale=True,size=[-1,1,-1,1]):
    M = len(b) - 1
    N = len(a) - 1
    plt.figure(figsize=(5,5))
    #plt.axis('equal')
    N_roots = np.array([0.0])
    if M > 0:
        N_roots = np.roots(b)
    D_roots = np.array([0.0])
    if N > 0:
        D_roots = np.roots(a)
    if auto_scale:
        size[0] = min(np.min(np.real(N_roots)),np.min(np.real(D_roots)))-0.5
        size[1] = max(np.max(np.real(N_roots)),np.max(np.real(D_roots)))+0.5
        size[1] = max(size[1],0.5)
        size[2] = min(np.min(np.imag(N_roots)),np.min(np.imag(D_roots)))-0.5
        size[3] = max(np.max(np.imag(N_roots)),np.max(np.imag(D_roots)))+0.5

    plt.plot([size[0],size[1]],[0,0],'k--')
    plt.plot([0,0],[size[2],size[3]],'r--')
    # Plot labels if multiplicity greater than 1
    x_scale = size[1]-size[0]
    y_scale = size[3]-size[2]
    x_off = 0.03
    y_off = 0.01
    if M > 0:
        #N_roots = np.roots(b)
        N_uniq, N_mult=signal.unique_roots(N_roots,tol=1e-3, rtype='avg')
        plt.plot(np.real(N_uniq),np.imag(N_uniq),'ko',mfc='None',ms=8)
        idx_N_mult = np.nonzero(np.ravel(N_mult>1))[0]
        for k in range(len(idx_N_mult)):
            x_loc = np.real(N_uniq[idx_N_mult[k]]) + x_off*x_scale
            y_loc =np.imag(N_uniq[idx_N_mult[k]]) + y_off*y_scale
            plt.text(x_loc,y_loc,str(N_mult[idx_N_mult[k]]),ha='center',va='bottom',fontsize=10)
    if N > 0:
        #D_roots = np.roots(a)
        D_uniq, D_mult=signal.unique_roots(D_roots,tol=1e-3, rtype='avg')
        plt.plot(np.real(D_uniq),np.imag(D_uniq),'kx',ms=8)
        idx_D_mult = np.nonzero(np.ravel(D_mult>1))[0]
        for k in range(len(idx_D_mult)):
            x_loc = np.real(D_uniq[idx_D_mult[k]]) + x_off*x_scale
            y_loc =np.imag(D_uniq[idx_D_mult[k]]) + y_off*y_scale
            plt.text(x_loc,y_loc,str(D_mult[idx_D_mult[k]]),ha='center',va='bottom',fontsize=10)
    plt.xlabel('Real Part')
    plt.ylabel('Imaginary Part')
    plt.title('Pole-Zero Plot')
    #plt.grid()
    plt.axis(np.array(size))
    return M,N

Response: Create an s - plane pole - zero plot.


Instruction:
Explain what the following Python 3 code does
def OS_filter(x,h,N,mode=0):

    P = len(h)
    # zero pad start of x so first frame can recover first true samples of x
    x = np.hstack((np.zeros(P-1),x))
    L = N - P + 1
    Nx = len(x)
    Nframe = int(np.ceil(Nx/float(L)))
    # zero pad end of x to full number of frames needed
    x = np.hstack((x,np.zeros(Nframe*L-Nx)))
    y = np.zeros(int(Nframe*N))
    # create an instrumentation matrix to observe the overlap and save behavior
    y_mat = np.zeros((Nframe,int(Nframe*N)))

    H = fft.fft(h,N)
    # begin the filtering operation
    for k in range(Nframe):
        xk = x[k*L:k*L+N]
        Xk = fft.fft(xk,N)
        Yk = H*Xk
        yk = np.real(fft.ifft(Yk)) # imag part should be zero
        y[k*L+P-1:k*L+N] = yk[P-1:]
        y_mat[k,k*L:k*L+N] = yk
    if mode == 1:
        return y[P-1:Nx], y_mat[:,P-1:Nx]
    else:
        return y[P-1:Nx]

Response: This function implements the classical overlap and save method of the transform domain FIR filter.


Instruction:
Write a Python 3 function for
adding white Gaussian noise to a single real sinusoid. Input a single sinusoid to this function and it returns a noisy sinusoid at a specific SNR value in dB. Sinusoid power is calculated using np.var. Parameters ---------- x : Input signal as ndarray consisting of a single sinusoid SNRdB : SNR in dB for output sinusoid Returns ------- y : Noisy sinusoid return vector Examples -------- >>> # set the SNR to 10 dB >>> n = arange(0,10000) >>> x = cos(2*pi*0.04*n) >>> y = sinusoidAWGN(x,10.0)

Response:
def sinusoidAWGN(x,SNRdB):
    """
    Add white Gaussian noise to a single real sinusoid.

    Input a single sinusoid to this function and it returns a noisy
    sinusoid at a specific SNR value in dB. Sinusoid power is calculated
    using np.var.

    Parameters
    ----------
    x : Input signal as ndarray consisting of a single sinusoid
    SNRdB : SNR in dB for output sinusoid

    Returns
    -------
    y : Noisy sinusoid return vector

    Examples
    --------
    >>> # set the SNR to 10 dB
    >>> n = arange(0,10000)
    >>> x = cos(2*pi*0.04*n)
    >>> y = sinusoidAWGN(x,10.0)
    """
    # Estimate signal power
    x_pwr = np.var(x)

    # Create noise vector
    noise = np.sqrt(x_pwr/10**(SNRdB/10.))*np.random.randn(len(x));
    return x + noise


Instruction:
Write a Python 3 function that can
return the disdata of a given angle modulated signal.

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

    Mark Wickert
    """
    X=np.real(x)        # X is the real part of the received signal
    Y=np.imag(x)        # Y is the imaginary part of the received signal
    b=np.array([1, -1]) # filter coefficients for discrete derivative
    a=np.array([1, 0])  # filter coefficients for discrete derivative
    derY=signal.lfilter(b,a,Y)  # derivative of Y,
    derX=signal.lfilter(b,a,X)  #    "          X,
    disdata=(X*derY-Y*derX)/(X**2+Y**2)
    return disdata


Instruction:
Can you generate the documentation for the following Python 3 function
def mono_FM(x,fs=2.4e6,file_name='test.wav'):
    b = signal.firwin(64,2*200e3/float(fs))
    # Filter and decimate (should be polyphase)
    y = signal.lfilter(b,1,x)
    z = ss.downsample(y,10)
    # Apply complex baseband discriminator
    z_bb = discrim(z)
    # Design 2nd decimation lowpass filter (fc = 12 KHz)
    bb = signal.firwin(64,2*12e3/(float(fs)/10))
    # Filter and decimate
    zz_bb = signal.lfilter(bb,1,z_bb)
    # Decimate by 5
    z_out = ss.downsample(zz_bb,5)
    # Save to wave file
    ss.to_wav(file_name, 48000, z_out/2)
    print('Done!')
    return z_bb, z_out

Response:
Decimate complex baseband input by 10
    Design 1st decimation lowpass filter (f_c = 200 KHz)


Instruction:
Explain what the following Python 3 code does
def sccs_bit_sync(y,Ns):
    # decimated symbol sequence for SEP
    rx_symb_d = np.zeros(int(np.fix(len(y)/Ns)))
    track = np.zeros(int(np.fix(len(y)/Ns)))
    bit_count = -1
    y_abs = np.zeros(len(y))
    clk = np.zeros(len(y))
    k = Ns+1 #initial 1-of-Ns symbol synch clock phase
    # Sample-by-sample processing required
    for i in range(len(y)):
        #y_abs(i) = abs(round(real(y(i))))
        if i >= Ns: # do not process first Ns samples
            # Collect timing decision unit (TDU) samples
            y_abs[i] = np.abs(np.sum(y[i-Ns+1:i+1]))
            # Update sampling instant and take a sample
            # For causality reason the early sample is 'i',
            # the on-time or prompt sample is 'i-1', and
            # the late sample is 'i-2'.
            if (k == 0):
                # Load the samples into the 3x1 TDU register w_hat.
                # w_hat[1] = late, w_hat[2] = on-time; w_hat[3] = early.
                w_hat = y_abs[i-2:i+1]
                bit_count += 1
                if w_hat[1] != 0:
                    if w_hat[0] < w_hat[2]:
                        k = Ns-1
                        clk[i-2] = 1
                        rx_symb_d[bit_count] = y[i-2-int(np.round(Ns/2))-1]
                    elif w_hat[0] > w_hat[2]:
                        k = Ns+1
                        clk[i] = 1
                        rx_symb_d[bit_count] = y[i-int(np.round(Ns/2))-1]
                    else:
                        k = Ns
                        clk[i-1] = 1
                        rx_symb_d[bit_count] = y[i-1-int(np.round(Ns/2))-1]
                else:
                    k = Ns
                    clk[i-1] = 1
                    rx_symb_d[bit_count] = y[i-1-int(np.round(Ns/2))]
                track[bit_count] = np.mod(i,Ns)
        k -= 1
    # Trim the final output to bit_count
    rx_symb_d = rx_symb_d[:bit_count]
    return rx_symb_d, clk, track

Response: Synthesize a sequence of baseband NRZ data into a single baseband NRZ waveform and a single baseband NRZ waveform.


Instruction:
Here you have a function in Python 3, explain what it does
def fsk_BEP(rx_data,m,flip):
    Nbits = len(rx_data)
    c = dc.m_seq(m)
    if flip == 1:
        # Flip the sequence to compenstate for mbed code difference
        # First make it a 1xN array
        c.shape = (1,len(c))
        c = np.fliplr(c).flatten()
    L = int(np.ceil(Nbits/float(len(c))))
    tx_data = np.dot(c.reshape(len(c),1),np.ones((1,L)))
    tx_data = tx_data.T.reshape((1,len(c)*L)).flatten()
    tx_data = tx_data[:Nbits]
    # Convert to +1/-1 bits
    tx_data = 2*tx_data - 1
    Bit_count,Bit_errors = dc.BPSK_BEP(rx_data,tx_data)
    print('len rx_data = %d, len tx_data = %d' % (len(rx_data),len(tx_data)))
    Pe = Bit_errors/float(Bit_count)
    print('/////////////////////////////////////')
    print('Bit Errors: %d' % Bit_errors)
    print('Bits Total: %d' % Bit_count)
    print('       BEP: %2.2e' % Pe)
    print('/////////////////////////////////////')

Response: This function is used to reconstruct the BEP of the data bits recovered by the RTL - SDR Based FSK Receiver.


Instruction:
Make a summary of the following Python 3 code
def complex2wav(filename,rate,x):
    x_wav = np.hstack((np.array([x.real]).T,np.array([x.imag]).T))
    ss.to_wav(filename, rate, x_wav)
    print('Saved as binary wav file with (I,Q)<=>(L,R)')

Response: Write a complex signal vector to a wav file.


Instruction:
Write a Python 3 script for
returning 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.

Response:
def wav2complex(filename):
    """
    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
    """
    fs, x_LR_cols = ss.from_wav(filename)
    x = x_LR_cols[:,0] + 1j*x_LR_cols[:,1]
    return fs,x


Instruction:
Can you generate a brief explanation for the following Python 3 code
def FIR_header(fname_out, h):
    M = len(h)
    N = 3  # Coefficients per line
    f = open(fname_out, 'wt')
    f.write('//define a FIR coefficient Array\n\n')
    f.write('#include <stdint.h>\n\n')
    f.write('#ifndef M_FIR\n')
    f.write('#define M_FIR %d\n' % M)
    f.write('#endif\n')
    f.write('/************************************************************************/\n');
    f.write('/*                         FIR Filter Coefficients                      */\n');
    f.write('float32_t h_FIR[M_FIR] = {')
    kk = 0;
    for k in range(M):
        # k_mod = k % M
        if (kk < N - 1) and (k < M - 1):
            f.write('%15.12f,' % h[k])
            kk += 1
        elif (kk == N - 1) & (k < M - 1):
            f.write('%15.12f,\n' % h[k])
            if k < M:
                f.write('                          ')
                kk = 0
        else:
            f.write('%15.12f' % h[k])
    f.write('};\n')
    f.write('/************************************************************************/\n')
    f.close()

Response: Write the FIR filter header files


Instruction:
Can you write a function in Python 3 where it
writes the fixed - point filter header files.

Response:
def FIR_fix_header(fname_out, h):
    """
    Write FIR Fixed-Point Filter Header Files

    Mark Wickert February 2015
    """
    M = len(h)
    hq = int16(rint(h * 2 ** 15))
    N = 8  # Coefficients per line
    f = open(fname_out, 'wt')
    f.write('//define a FIR coefficient Array\n\n')
    f.write('#include <stdint.h>\n\n')
    f.write('#ifndef M_FIR\n')
    f.write('#define M_FIR %d\n' % M)
    f.write('#endif\n')
    f.write('/************************************************************************/\n');
    f.write('/*                         FIR Filter Coefficients                      */\n');
    f.write('int16_t h_FIR[M_FIR] = {')
    kk = 0;
    for k in range(M):
        # k_mod = k % M
        if (kk < N - 1) and (k < M - 1):
            f.write('%5d,' % hq[k])
            kk += 1
        elif (kk == N - 1) & (k < M - 1):
            f.write('%5d,\n' % hq[k])
            if k < M:
                f.write('                        ')
                kk = 0
        else:
            f.write('%5d' % hq[k])
    f.write('};\n')
    f.write('/************************************************************************/\n')
    f.close()


Instruction:
Can you tell what is the following Python 3 function doing
def IIR_sos_header(fname_out, SOS_mat):
    Ns, Mcol = SOS_mat.shape
    f = open(fname_out, 'wt')
    f.write('//define a IIR SOS CMSIS-DSP coefficient array\n\n')
    f.write('#include <stdint.h>\n\n')
    f.write('#ifndef STAGES\n')
    f.write('#define STAGES %d\n' % Ns)
    f.write('#endif\n')
    f.write('/*********************************************************/\n');
    f.write('/*                     IIR SOS Filter Coefficients       */\n');
    f.write('float32_t ba_coeff[%d] = { //b0,b1,b2,a1,a2,... by stage\n' % (5 * Ns))
    for k in range(Ns):
        if (k < Ns - 1):
            f.write('    %+-13e, %+-13e, %+-13e,\n' % \
                    (SOS_mat[k, 0], SOS_mat[k, 1], SOS_mat[k, 2]))
            f.write('    %+-13e, %+-13e,\n' % \
                    (-SOS_mat[k, 4], -SOS_mat[k, 5]))
        else:
            f.write('    %+-13e, %+-13e, %+-13e,\n' % \
                    (SOS_mat[k, 0], SOS_mat[k, 1], SOS_mat[k, 2]))
            f.write('    %+-13e, %+-13e\n' % \
                    (-SOS_mat[k, 4], -SOS_mat[k, 5]))
    # for k in range(Ns):
    #     if (k < Ns-1):
    #         f.write('    %15.12f, %15.12f, %15.12f,\n' % \
    #                 (SOS_mat[k,0],SOS_mat[k,1],SOS_mat[k,2]))
    #         f.write('    %15.12f, %15.12f,\n' % \
    #                 (-SOS_mat[k,4],-SOS_mat[k,5]))
    #     else:
    #         f.write('    %15.12f, %15.12f, %15.12f,\n' % \
    #                 (SOS_mat[k,0],SOS_mat[k,1],SOS_mat[k,2]))
    #         f.write('    %15.12f, %15.12f\n' % \
    #                 (-SOS_mat[k,4],-SOS_mat[k,5]))
    f.write('};\n')
    f.write('/*********************************************************/\n')
    f.close()

Response: Write the header of the SOS - DSP file.


Instruction:
Can you create a Python 3 function that
writes the header of a single CA file.

Response:
def CA_code_header(fname_out, Nca):
    """
    Write 1023 bit CA (Gold) Code Header Files

    Mark Wickert February 2015
    """
    dir_path = os.path.dirname(os.path.realpath(__file__))
    ca = loadtxt(dir_path + '/ca1thru37.txt', dtype=int16, usecols=(Nca - 1,), unpack=True)

    M = 1023  # code period
    N = 23  # code bits per line
    Sca = 'ca' + str(Nca)
    f = open(fname_out, 'wt')
    f.write('//define a CA code\n\n')
    f.write('#include <stdint.h>\n\n')
    f.write('#ifndef N_CA\n')
    f.write('#define N_CA %d\n' % M)
    f.write('#endif\n')
    f.write('/*******************************************************************/\n');
    f.write('/*                    1023 Bit CA Gold Code %2d                     */\n' \
            % Nca);
    f.write('int8_t ca%d[N_CA] = {' % Nca)
    kk = 0;
    for k in range(M):
        # k_mod = k % M
        if (kk < N - 1) and (k < M - 1):
            f.write('%d,' % ca[k])
            kk += 1
        elif (kk == N - 1) & (k < M - 1):
            f.write('%d,\n' % ca[k])
            if k < M:
                if Nca < 10:
                    f.write('                    ')
                else:
                    f.write('                     ')
                kk = 0
        else:
            f.write('%d' % ca[k])
    f.write('};\n')
    f.write('/*******************************************************************/\n')
    f.close()


Instruction:
How would you code a function in Python 3 to
eye pattern plot of a baseband digital communications waveform. The signal must be real, but can be multivalued in terms of the underlying modulation scheme. Used for BPSK eye plots in the Case Study article. Parameters ---------- x : ndarray of the real input data vector/array L : display length in samples (usually two symbols) S : start index Returns ------- None : A plot window opens containing the eye plot Notes ----- Increase S to eliminate filter transients. Examples -------- 1000 bits at 10 samples per bit with 'rc' shaping. >>> import matplotlib.pyplot as plt >>> from sk_dsp_comm import digitalcom as dc >>> x,b, data = dc.NRZ_bits(1000,10,'rc') >>> dc.eye_plot(x,20,60) >>> plt.show()

Response:
def eye_plot(x,L,S=0):
    """
    Eye pattern plot of a baseband digital communications waveform.

    The signal must be real, but can be multivalued in terms of the underlying
    modulation scheme. Used for BPSK eye plots in the Case Study article.

    Parameters
    ----------
    x : ndarray of the real input data vector/array
    L : display length in samples (usually two symbols)
    S : start index

    Returns
    -------
    None : A plot window opens containing the eye plot

    Notes
    -----
    Increase S to eliminate filter transients.

    Examples
    --------
    1000 bits at 10 samples per bit with 'rc' shaping.

    >>> import matplotlib.pyplot as plt
    >>> from sk_dsp_comm import digitalcom as dc
    >>> x,b, data = dc.NRZ_bits(1000,10,'rc')
    >>> dc.eye_plot(x,20,60)
    >>> plt.show()
    """
    plt.figure(figsize=(6,4))
    idx = np.arange(0,L+1)
    plt.plot(idx,x[S:S+L+1],'b')
    k_max = int((len(x) - S)/L)-1
    for k in range(1,k_max):
         plt.plot(idx,x[S+k*L:S+L+1+k*L],'b')
    plt.grid()
    plt.xlabel('Time Index - n')
    plt.ylabel('Amplitude')
    plt.title('Eye Plot')
    return 0


Instruction:
Make a summary of the following Python 3 code
def scatter(x,Ns,start):
    xI = np.real(x[start::Ns])
    xQ = np.imag(x[start::Ns])
    return xI, xQ

Response: Scatter the signal x with NRZ bits and return the real and imaginary part of x.


Instruction:
Make a summary of the following Python 3 code
def strips(x,Nx,fig_size=(6,4)):
    plt.figure(figsize=fig_size)
    #ax = fig.add_subplot(111)
    N = len(x)
    Mx = int(np.ceil(N/float(Nx)))
    x_max = np.max(np.abs(x))
    for kk in range(Mx):
        plt.plot(np.array([0,Nx]),-kk*Nx*np.array([1,1]),'r-.')
        plt.plot(x[kk*Nx:(kk+1)*Nx]/x_max*0.4*Nx-kk*Nx,'b')
    plt.axis([0,Nx,-Nx*(Mx-0.5),Nx*0.5])
    plt.yticks(np.arange(0,-Nx*Mx,-Nx),np.arange(0,Nx*Mx,Nx))
    plt.xlabel('Index')
    plt.ylabel('Strip Amplitude and Starting Index')
    return 0

Response:
Plots the contents of real ndarray x as a vertical stacking of
    strips each of length Nx.


Instruction:
Write a Python 3 function for
counting bit errors between a transmitted and received BPSK signal.

Response:
def bit_errors(tx_data,rx_data,Ncorr = 1024,Ntransient = 0):
    """
    Count bit errors between a transmitted and received BPSK signal.
    Time delay between streams is detected as well as ambiquity resolution
    due to carrier phase lock offsets of :math:`k*\\pi`, k=0,1.
    The ndarray tx_data is Tx 0/1 bits as real numbers I.
    The ndarray rx_data is Rx 0/1 bits as real numbers I.
    Note: Ncorr needs to be even
    """

    # Remove Ntransient symbols and level shift to {-1,+1}
    tx_data = 2*tx_data[Ntransient:]-1
    rx_data = 2*rx_data[Ntransient:]-1
    # Correlate the first Ncorr symbols at four possible phase rotations
    R0 = np.fft.ifft(np.fft.fft(rx_data,Ncorr)*
                     np.conj(np.fft.fft(tx_data,Ncorr)))
    R1 = np.fft.ifft(np.fft.fft(-1*rx_data,Ncorr)*
                     np.conj(np.fft.fft(tx_data,Ncorr)))
    #Place the zero lag value in the center of the array
    R0 = np.fft.fftshift(R0)
    R1 = np.fft.fftshift(R1)
    R0max = np.max(R0.real)
    R1max = np.max(R1.real)
    R = np.array([R0max,R1max])
    Rmax = np.max(R)
    kphase_max = np.where(R == Rmax)[0]
    kmax = kphase_max[0]
    # Correlation lag value is zero at the center of the array
    if kmax == 0:
        lagmax = np.where(R0.real == Rmax)[0] - Ncorr/2
    elif kmax == 1:
        lagmax = np.where(R1.real == Rmax)[0] - Ncorr/2
    taumax = lagmax[0]
    print('kmax =  %d, taumax = %d' % (kmax, taumax))

    # Count bit and symbol errors over the entire input ndarrays
    # Begin by making tx and rx length equal and apply phase rotation to rx
    if taumax < 0:
        tx_data = tx_data[int(-taumax):]
        tx_data = tx_data[:min(len(tx_data),len(rx_data))]
        rx_data = (-1)**kmax*rx_data[:len(tx_data)]
    else:
        rx_data = (-1)**kmax * rx_data[int(taumax):]
        rx_data = rx_data[:min(len(tx_data),len(rx_data))]
        tx_data = tx_data[:len(rx_data)]
    # Convert to 0's and 1's
    Bit_count = len(tx_data)
    tx_I = np.int16((tx_data.real + 1)/2)
    rx_I = np.int16((rx_data.real + 1)/2)
    Bit_errors = tx_I ^ rx_I
    return Bit_count,np.sum(Bit_errors)


Instruction:
Given the following Python 3 function, write the documentation
def QAM_bb(N_symb,Ns,mod_type='16qam',pulse='rect',alpha=0.35):
    # Filter the impulse train waveform with a square root raised
    # cosine pulse shape designed as follows:

    # Design the filter to be of duration 12 symbols and
    # fix the excess bandwidth factor at alpha = 0.35
    # If SRC = 0 use a simple rectangle pulse shape
    if pulse.lower() == 'src':
        b = sqrt_rc_imp(Ns,alpha,6)
    elif pulse.lower() == 'rc':
        b = rc_imp(Ns,alpha,6)
    elif pulse.lower() == 'rect':
        b = np.ones(int(Ns)) #alt. rect. pulse shape
    else:
        raise ValueError('pulse shape must be src, rc, or rect')

    if mod_type.lower() == 'qpsk':
        M = 2 # bits per symbol
    elif mod_type.lower() == '16qam':
        M = 4
    elif mod_type.lower() == '64qam':
        M = 8
    elif mod_type.lower() == '256qam':
        M = 16
    else:
        raise ValueError('Unknown mod_type')

    # Create random symbols for the I & Q channels
    xI = np.random.randint(0,M,N_symb)
    xI = 2*xI - (M-1)
    xQ = np.random.randint(0,M,N_symb)
    xQ = 2*xQ - (M-1)
    # Employ differential encoding to counter phase ambiquities
    # Create a zero padded (interpolated by Ns) symbol sequence.
    # This prepares the symbol sequence for arbitrary pulse shaping.
    symbI = np.hstack((xI.reshape(N_symb,1),np.zeros((N_symb,int(Ns)-1))))
    symbI = symbI.flatten()
    symbQ = np.hstack((xQ.reshape(N_symb,1),np.zeros((N_symb,int(Ns)-1))))
    symbQ = symbQ.flatten()
    symb = symbI + 1j*symbQ
    if M > 2:
        symb /= (M-1)

    # The impulse train waveform contains one pulse per Ns (or Ts) samples
    # imp_train = [ones(K,1) zeros(K,Ns-1)]';
    # imp_train = reshape(imp_train,Ns*K,1);

    # Filter the impulse train signal
    x = signal.lfilter(b,1,symb)
    x = x.flatten() # out is a 1D vector
    # Scale shaping filter to have unity DC gain
    b = b/sum(b)
    return x, b, xI+1j*xQ

Response: A simple baseband transmitter for a set of samples.


Instruction:
Explain what the following Python 3 code does
def MPSK_bb(N_symb,Ns,M,pulse='rect',alpha = 0.25,MM=6):
    data = np.random.randint(0,M,N_symb)
    xs = np.exp(1j*2*np.pi/M*data)
    x = np.hstack((xs.reshape(N_symb,1),np.zeros((N_symb,int(Ns)-1))))
    x =x.flatten()
    if pulse.lower() == 'rect':
        b = np.ones(int(Ns))
    elif pulse.lower() == 'rc':
        b = rc_imp(Ns,alpha,MM)
    elif pulse.lower() == 'src':
        b = sqrt_rc_imp(Ns,alpha,MM)
    else:
        raise ValueError('pulse type must be rec, rc, or src')
    x = signal.lfilter(b,1,x)
    if M == 4:
        x = x*np.exp(1j*np.pi/4); # For QPSK points in quadrants
    return x,b/float(Ns),data

Response: Generate a complex baseband MPSK signal with pulse shaping.


Instruction:
How would you implement a function in Python 3 that
generates a BIPS - K transmitter with adjacent channel interference.

Response:
def BPSK_tx(N_bits,Ns,ach_fc=2.0,ach_lvl_dB=-100,pulse='rect',alpha = 0.25,M=6):
    """
    Generates biphase shift keyed (BPSK) transmitter with adjacent channel interference.

    Generates three BPSK signals with rectangular or square root raised cosine (SRC)
    pulse shaping of duration N_bits and Ns samples per bit. The desired signal is
    centered on f = 0, which the adjacent channel signals to the left and right
    are also generated at dB level relative to the desired signal. Used in the
    digital communications Case Study supplement.

    Parameters
    ----------
    N_bits : the number of bits to simulate
    Ns : the number of samples per bit
    ach_fc : the frequency offset of the adjacent channel signals (default 2.0)
    ach_lvl_dB : the level of the adjacent channel signals in dB (default -100)
    pulse : the pulse shape 'rect' or 'src'
    alpha : square root raised cosine pulse shape factor (default = 0.25)
    M : square root raised cosine pulse truncation factor (default = 6)

    Returns
    -------
    x : ndarray of the composite signal x0 + ach_lvl*(x1p + x1m)
    b : the transmit pulse shape
    data0 : the data bits used to form the desired signal; used for error checking

    Notes
    -----

    Examples
    --------
    >>> x,b,data0 = BPSK_tx(1000,10,'src')
    """
    x0,b,data0 = NRZ_bits(N_bits,Ns,pulse,alpha,M)
    x1p,b,data1p = NRZ_bits(N_bits,Ns,pulse,alpha,M)
    x1m,b,data1m = NRZ_bits(N_bits,Ns,pulse,alpha,M)
    n = np.arange(len(x0))
    x1p = x1p*np.exp(1j*2*np.pi*ach_fc/float(Ns)*n)
    x1m = x1m*np.exp(-1j*2*np.pi*ach_fc/float(Ns)*n)
    ach_lvl = 10**(ach_lvl_dB/20.)
    return x0 + ach_lvl*(x1p + x1m), b, data0


Instruction:
Can you generate a brief explanation for the following Python 3 code
def RZ_bits(N_bits,Ns,pulse='rect',alpha = 0.25,M=6):
    data = np.random.randint(0,2,N_bits)
    x = np.hstack((data.reshape(N_bits,1),np.zeros((N_bits,int(Ns)-1))))
    x =x.flatten()
    if pulse.lower() == 'rect':
        b = np.ones(int(Ns))
    elif pulse.lower() == 'rc':
        b = rc_imp(Ns,alpha,M)
    elif pulse.lower() == 'src':
        b = sqrt_rc_imp(Ns,alpha,M)
    else:
        print('pulse type must be rec, rc, or src')
    x = signal.lfilter(b,1,x)
    return x,b/float(Ns),data

Response: Generate return - to - zero RZ data bits with pulse shaping.


Instruction:
Explain what the following Python 3 code does
def xcorr(x1,x2,Nlags):
    K = 2*(int(np.floor(len(x1)/2)))
    X1 = fft.fft(x1[:K])
    X2 = fft.fft(x2[:K])
    E1 = sum(abs(x1[:K])**2)
    E2 = sum(abs(x2[:K])**2)
    r12 = np.fft.ifft(X1*np.conj(X2))/np.sqrt(E1*E2)
    k = np.arange(K) - int(np.floor(K/2))
    r12 = np.fft.fftshift(r12)
    idx = np.nonzero(np.ravel(abs(k) <= Nlags))
    return r12[idx], k[idx]

Response: Compute the energy normalized cross correlation between two sequences x1 and x2.


Instruction:
Implement a Python 3 function for
encoding signal samples to a serial bit stream of 0 or 1 bits.

Response:
def PCM_encode(x,N_bits):
    """
    Parameters
    ----------
    x : signal samples to be PCM encoded
    N_bits ; bit precision of PCM samples

    Returns
    -------
    x_bits = encoded serial bit stream of 0/1 values. MSB first.

    Mark Wickert, Mark 2015
    """
    xq = np.int16(np.rint(x*2**(N_bits-1)))
    x_bits = np.zeros((N_bits,len(xq)))
    for k, xk in enumerate(xq):
        x_bits[:,k] = to_bin(xk,N_bits)
    # Reshape into a serial bit stream
    x_bits = np.reshape(x_bits,(1,len(x)*N_bits),'F')
    return np.int16(x_bits.flatten())


Instruction:
Can you generate the documentation for the following Python 3 function
def to_bin(data, width):
    data_str = bin(data & (2**width-1))[2:].zfill(width)
    return [int(x) for x in tuple(data_str)]

Response: Convert an unsigned integer to a numpy binary array with the first element of the MSB and the last element of the LSB.


Instruction:
Here you have a function in Python 3, explain what it does
def from_bin(bin_array):
    width = len(bin_array)
    bin_wgts = 2**np.arange(width-1,-1,-1)
    return int(np.dot(bin_array,bin_wgts))

Response: Convert binary array back a nonnegative integer.


Instruction:
Can you generate the documentation for the following Python 3 function
def PCM_decode(x_bits,N_bits):
    N_samples = len(x_bits)//N_bits
    # Convert serial bit stream into parallel words with each
    # column holdingthe N_bits binary sample value
    xrs_bits = x_bits.copy()
    xrs_bits = np.reshape(xrs_bits,(N_bits,N_samples),'F')
    # Convert N_bits binary words into signed integer values
    xq = np.zeros(N_samples)
    w = 2**np.arange(N_bits-1,-1,-1) # binary weights for bin
                                     # to dec conversion
    for k in range(N_samples):
       xq[k] = np.dot(xrs_bits[:,k],w) - xrs_bits[0,k]*2**N_bits
    return xq/2**(N_bits-1)

Response: Decodes a PCM signal into a sequence of signed integer values.


Instruction:
Given the following Python 3 function, write the documentation
def NDA_symb_sync(z,Ns,L,BnTs,zeta=0.707,I_ord=3):
    # Loop filter parameters
    K0 = -1.0 # The modulo 1 counter counts down so a sign change in loop
    Kp = 1.0
    K1 = 4*zeta/(zeta + 1/(4*zeta))*BnTs/Ns/Kp/K0
    K2 = 4/(zeta + 1/(4*zeta))**2*(BnTs/Ns)**2/Kp/K0
    zz = np.zeros(len(z),dtype=np.complex128)
    #zz = np.zeros(int(np.floor(len(z)/float(Ns))),dtype=np.complex128)
    e_tau = np.zeros(len(z))
    #e_tau = np.zeros(int(np.floor(len(z)/float(Ns))))
    #z_TED_buff = np.zeros(Ns)
    c1_buff = np.zeros(2*L+1)

    vi = 0
    CNT_next = 0
    mu_next = 0
    underflow = 0
    epsilon = 0
    mm = 1
    z = np.hstack(([0], z))
    for nn in range(1,Ns*int(np.floor(len(z)/float(Ns)-(Ns-1)))):
        # Define variables used in linear interpolator control
        CNT = CNT_next
        mu = mu_next
        if underflow == 1:
            if I_ord == 1:
                # Decimated interpolator output (piecewise linear)
                z_interp = mu*z[nn] + (1 - mu)*z[nn-1]
            elif I_ord == 2:
                # Decimated interpolator output (piecewise parabolic)
                # in Farrow form with alpha = 1/2
                v2 = 1/2.*np.sum(z[nn+2:nn-1-1:-1]*[1, -1, -1, 1])
                v1 = 1/2.*np.sum(z[nn+2:nn-1-1:-1]*[-1, 3, -1, -1])
                v0 = z[nn]
                z_interp = (mu*v2 + v1)*mu + v0
            elif I_ord == 3:
                # Decimated interpolator output (piecewise cubic)
                # in Farrow form
                v3 = np.sum(z[nn+2:nn-1-1:-1]*[1/6., -1/2., 1/2., -1/6.])
                v2 = np.sum(z[nn+2:nn-1-1:-1]*[0, 1/2., -1, 1/2.])
                v1 = np.sum(z[nn+2:nn-1-1:-1]*[-1/6., 1, -1/2., -1/3.])
                v0 = z[nn]
                z_interp = ((mu*v3 + v2)*mu + v1)*mu + v0
            else:
                print('Error: I_ord must 1, 2, or 3')
            # Form TED output that is smoothed using 2*L+1 samples
            # We need Ns interpolants for this TED: 0:Ns-1
            c1 = 0
            for kk in range(Ns):
                if I_ord == 1:
                    # piecewise linear interp over Ns samples for TED
                    z_TED_interp = mu*z[nn+kk] + (1 - mu)*z[nn-1+kk]
                elif I_ord == 2:
                    # piecewise parabolic in Farrow form with alpha = 1/2
                    v2 = 1/2.*np.sum(z[nn+kk+2:nn+kk-1-1:-1]*[1, -1, -1, 1])
                    v1 = 1/2.*np.sum(z[nn+kk+2:nn+kk-1-1:-1]*[-1, 3, -1, -1])
                    v0 = z[nn+kk]
                    z_TED_interp = (mu*v2 + v1)*mu + v0
                elif I_ord == 3:
                    # piecewise cubic in Farrow form
                    v3 = np.sum(z[nn+kk+2:nn+kk-1-1:-1]*[1/6., -1/2., 1/2., -1/6.])
                    v2 = np.sum(z[nn+kk+2:nn+kk-1-1:-1]*[0, 1/2., -1, 1/2.])
                    v1 = np.sum(z[nn+kk+2:nn+kk-1-1:-1]*[-1/6., 1, -1/2., -1/3.])
                    v0 = z[nn+kk]
                    z_TED_interp = ((mu*v3 + v2)*mu + v1)*mu + v0
                else:
                    print('Error: I_ord must 1, 2, or 3')
                c1 = c1 + np.abs(z_TED_interp)**2 * np.exp(-1j*2*np.pi/Ns*kk)
            c1 = c1/Ns
            # Update 2*L+1 length buffer for TED output smoothing
            c1_buff = np.hstack(([c1], c1_buff[:-1]))
            # Form the smoothed TED output
            epsilon = -1/(2*np.pi)*np.angle(np.sum(c1_buff)/(2*L+1))
            # Save symbol spaced (decimated to symbol rate) interpolants in zz
            zz[mm] = z_interp
            e_tau[mm] = epsilon # log the error to the output vector e
            mm += 1
        else:
            # Simple zezo-order hold interpolation between symbol samples
            # we just coast using the old value
            #epsilon = 0
            pass
        vp = K1*epsilon       # proportional component of loop filter
        vi = vi + K2*epsilon  # integrator component of loop filter
        v = vp + vi           # loop filter output
        W = 1/float(Ns) + v          # counter control word

        # update registers
        CNT_next = CNT - W           # Update counter value for next cycle
        if CNT_next < 0:             # Test to see if underflow has occured
            CNT_next = 1 + CNT_next  # Reduce counter value modulo-1 if underflow
            underflow = 1            # Set the underflow flag
            mu_next = CNT/W          # update mu
        else:
            underflow = 0
            mu_next = mu
    # Remove zero samples at end
    zz = zz[:-(len(zz)-mm+1)]
    # Normalize so symbol values have a unity magnitude
    zz /=np.std(zz)
    e_tau = e_tau[:-(len(e_tau)-mm+1)]
    return zz, e_tau

Response: This function is used to generate a NDA filter for a given baseband signal at nominal number of samples and time period.


Instruction:
Here you have a function in Python 3, explain what it does
def time_step(z,Ns,t_step,Nstep):
    z_step = np.hstack((z[:Ns*Nstep], z[(Ns*Nstep+t_step):], np.zeros(t_step)))
    return z_step

Response: Create a one sample per symbol signal containing a phase rotation and step Nsymb into the waveform.


Instruction:
Can you tell what is the following Python 3 function doing
def phase_step(z,Ns,p_step,Nstep):
    nn = np.arange(0,len(z[::Ns]))
    theta = np.zeros(len(nn))
    idx = np.where(nn >= Nstep)
    theta[idx] = p_step*np.ones(len(idx))
    z_rot = z[::Ns]*np.exp(1j*theta)
    return z_rot

Response:
Create a one sample per symbol signal containing a phase rotation
    step Nsymb into the waveform.


Instruction:
How would you explain what the following Python 3 function does
def PLL1(theta,fs,loop_type,Kv,fn,zeta,non_lin):
    T = 1/float(fs)
    Kv = 2*np.pi*Kv # convert Kv in Hz/v to rad/s/v

    if loop_type == 1:
        # First-order loop parameters
        # Note Bn = K/4 Hz but K has units of rad/s
        #fn = 4*Bn/(2*pi);
        K = 2*np.pi*fn # loop natural frequency in rad/s
    elif loop_type == 2:
        # Second-order loop parameters
        #fn = 1/(2*pi) * 2*Bn/(zeta + 1/(4*zeta));
        K = 4 *np.pi*zeta*fn # loop natural frequency in rad/s
        tau2 = zeta/(np.pi*fn)
    elif loop_type == 3:
        # Second-order loop parameters for one-pole lowpass with
        # phase lead correction.
        #fn = 1/(2*pi) * 2*Bn/(zeta + 1/(4*zeta));
        K = Kv # Essentially the VCO gain sets the single-sided
                # hold-in range in Hz, as it is assumed that Kp = 1
                # and KLF = 1.
        tau1 = K/((2*np.pi*fn)**2)
        tau2 = 2*zeta/(2*np.pi*fn)*(1 - 2*np.pi*fn/K*1/(2*zeta))
    else:
        print('Loop type must be 1, 2, or 3')

    # Initialize integration approximation filters
    filt_in_last = 0; filt_out_last = 0;
    vco_in_last = 0; vco_out = 0; vco_out_last = 0;

    # Initialize working and final output vectors
    n = np.arange(len(theta))
    theta_hat = np.zeros_like(theta)
    ev = np.zeros_like(theta)
    phi = np.zeros_like(theta)

    # Begin the simulation loop
    for k in  range(len(n)):
        phi[k] = theta[k] - vco_out
        if non_lin == 1:
            # sinusoidal phase detector
            pd_out = np.sin(phi[k])
        else:
            # Linear phase detector
            pd_out = phi[k]
        # Loop gain
        gain_out = K/Kv*pd_out # apply VCO gain at VCO
        # Loop filter
        if loop_type == 2:
            filt_in = (1/tau2)*gain_out
            filt_out = filt_out_last + T/2*(filt_in + filt_in_last)
            filt_in_last = filt_in
            filt_out_last = filt_out
            filt_out = filt_out + gain_out
        elif loop_type == 3:
            filt_in = (tau2/tau1)*gain_out - (1/tau1)*filt_out_last
            u3 = filt_in + (1/tau2)*filt_out_last
            filt_out = filt_out_last + T/2*(filt_in + filt_in_last)
            filt_in_last = filt_in
            filt_out_last = filt_out
        else:
            filt_out = gain_out;
        # VCO
        vco_in = filt_out
        if loop_type == 3:
            vco_in = u3
        vco_out = vco_out_last + T/2*(vco_in + vco_in_last)
        vco_in_last = vco_in
        vco_out_last = vco_out
        vco_out = Kv*vco_out # apply Kv
        # Measured loop signals
        ev[k] = vco_in
        theta_hat[k] = vco_out
    return theta_hat, ev, phi

Response: This function is used to generate the baseband analog PLL model for the first loop in the baseband.


Instruction:
Create a Python 3 function to
return a new alphabet with upper bound for the code rate R and Ck.

Response:
def conv_Pb_bound(R,dfree,Ck,SNRdB,hard_soft,M=2):
    """
    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
    """
    Pb = np.zeros_like(SNRdB)
    SNR = 10.**(SNRdB/10.)

    for n,SNRn in enumerate(SNR):
        for k in range(dfree,len(Ck)+dfree):
            if hard_soft == 0: # Evaluate hard decision bound
                Pb[n] += Ck[k-dfree]*hard_Pk(k,R,SNRn,M)
            elif hard_soft == 1: # Evaluate soft decision bound
                Pb[n] += Ck[k-dfree]*soft_Pk(k,R,SNRn,M)
            else: # Compute Uncoded Pe
                if M == 2:
                    Pb[n] = Q_fctn(np.sqrt(2.*SNRn))
                else:
                    Pb[n] = 4./np.log2(M)*(1 - 1/np.sqrt(M))*\
                            np.gaussQ(np.sqrt(3*np.log2(M)/(M-1)*SNRn));
    return Pb


Instruction:
Given the following Python 3 function, write the documentation
def hard_Pk(k,R,SNR,M=2):

    k = int(k)

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

Response: Calculates the hard version of the image.


Instruction:
How would you code a function in Python 3 to
calculate Pk for a given time series and time series.

Response:
def soft_Pk(k,R,SNR,M=2):
    """
    Pk = soft_Pk(k,R,SNR)

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

    Mark Wickert November 2014
    """
    if M == 2:
        Pk = Q_fctn(np.sqrt(2.*k*R*SNR))
    else:
        Pk = 4./np.log2(M)*(1 - 1./np.sqrt(M))*\
             Q_fctn(np.sqrt(3*k*R*np.log2(M)/float(M-1)*SNR))

    return Pk


Instruction:
Can you tell what is the following Python 3 function doing
def viterbi_decoder(self,x,metric_type='soft',quant_level=3):
        if metric_type == 'hard':
            # If hard decision must have 0/1 integers for input else float
            if np.issubdtype(x.dtype, np.integer):
                if x.max() > 1 or x.min() < 0:
                    raise ValueError('Integer bit values must be 0 or 1')
            else:
                raise ValueError('Decoder inputs must be integers on [0,1] for hard decisions')
        # Initialize cumulative metrics array
        cm_present = np.zeros((self.Nstates,1))

        NS = len(x) # number of channel symbols to process;
                     # must be even for rate 1/2
                     # must be a multiple of 3 for rate 1/3
        y = np.zeros(NS-self.decision_depth) # Decoded bit sequence
        k = 0
        symbolL = self.rate.denominator

        # Calculate branch metrics and update traceback states and traceback bits
        for n in range(0,NS,symbolL):
            cm_past = self.paths.cumulative_metric[:,0]
            tb_states_temp = self.paths.traceback_states[:,:-1].copy()
            tb_bits_temp = self.paths.traceback_bits[:,:-1].copy()
            for m in range(self.Nstates):
                d1 = self.bm_calc(self.branches.bits1[m],
                                    x[n:n+symbolL],metric_type,
                                    quant_level)
                d1 = d1 + cm_past[self.branches.states1[m]]
                d2 = self.bm_calc(self.branches.bits2[m],
                                    x[n:n+symbolL],metric_type,
                                    quant_level)
                d2 = d2 + cm_past[self.branches.states2[m]]
                if d1 <= d2: # Find the survivor assuming minimum distance wins
                    cm_present[m] = d1
                    self.paths.traceback_states[m,:] = np.hstack((self.branches.states1[m],
                                    tb_states_temp[int(self.branches.states1[m]),:]))
                    self.paths.traceback_bits[m,:] = np.hstack((self.branches.input1[m],
                                    tb_bits_temp[int(self.branches.states1[m]),:]))
                else:
                    cm_present[m] = d2
                    self.paths.traceback_states[m,:] = np.hstack((self.branches.states2[m],
                                    tb_states_temp[int(self.branches.states2[m]),:]))
                    self.paths.traceback_bits[m,:] = np.hstack((self.branches.input2[m],
                                    tb_bits_temp[int(self.branches.states2[m]),:]))
            # Update cumulative metric history
            self.paths.cumulative_metric = np.hstack((cm_present,
                                            self.paths.cumulative_metric[:,:-1]))

            # Obtain estimate of input bit sequence from the oldest bit in
            # the traceback having the smallest (most likely) cumulative metric
            min_metric = min(self.paths.cumulative_metric[:,0])
            min_idx = np.where(self.paths.cumulative_metric[:,0] == min_metric)
            if n >= symbolL*self.decision_depth-symbolL:  # 2 since Rate = 1/2
                y[k] = self.paths.traceback_bits[min_idx[0][0],-1]
                k += 1
        y = y[:k] # trim final length
        return y

Response: A method which performs Viterbi decoding of the given noisy bit stream and returns a new set of noisy bit values.