#! /usr/bin/env python3

import argparse
import csv
import pathlib
from numpy import *


# This is using the window method from
# http://users.spa.aalto.fi/vpv/publications/vesan_vaitos/ch3_pt1_fir.pdf
# Not the best method (worse than what gnuradio does anyway), but let's not care
# about that for now.


def hamming_offset(N, offset=0.0):
    alpha = 0.54
    beta = 1. - alpha
    # Notice the division by N instead of N - 1, that is intentional so that we
    # don't wrap to the next cosine period
    return array([alpha - beta * cos(2 * pi * (n + offset) / (N)) for n in range(N)])


def make_filter(offset, num_taps):
    return array([sinc(n + offset - num_taps / 2) for n in range(num_taps)]) * \
        hamming_offset(num_taps, offset=offset)


def make_filter_bank(steps, num_taps):
    return [
        make_filter(1 - n / (steps - 1), num_taps)
        for n in range(steps)
    ]


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-n", "--num-taps",
        type=int,
        default=8,
    )
    parser.add_argument(
        "-s", "--steps",
        type=int,
        default=128,
    )
    parser.add_argument(
        "-f", "--format",
        choices=["csv", "c++"],
        default="csv"
    )
    parser.add_argument(
        "-o", "--output",
        type=pathlib.Path,
        default=pathlib.Path("/dev/stdout"),
    )
    args = parser.parse_args()

    bank = make_filter_bank(args.steps, args.num_taps)

    with args.output.open("w") as f:
        if args.format == "csv":
            writer = csv.writer(f)
            writer.writerows(bank)
        elif args.format == "c++":
            def push(ls):
                f.write("\n".join(ls))
                f.write("\n")
            push([
                "// Generated by kvak/interpol.py",
                "",
                "const unsigned int num_taps = {};".format(args.num_taps),
                "const unsigned int num_steps = {};".format(len(bank)),
                "", "",
                "constexpr float filter_coeffs[num_steps][num_taps] = {",
            ])
            for i, filt in enumerate(bank):
                f.write(
                    "\t{ " + ", ".join(map(str, filt)) + " }},  // {} / {}\n"
                    .format(i, len(bank) - 1)
                )
            push([
                "};",
                "",
            ])

#! /usr/bin/env python3

import argparse
import csv
import pathlib
from numpy import *


# This is using the window method from
# http://users.spa.aalto.fi/vpv/publications/vesan_vaitos/ch3_pt1_fir.pdf
# Not the best method (worse than what gnuradio does anyway), but let's not care
# about that for now.


def hamming_offset(N, offset=0.0):
    alpha = 0.54
    beta = 1. - alpha
    # Notice the division by N instead of N - 1, that is intentional so that we
    # don't wrap to the next cosine period
    return array([alpha - beta * cos(2 * pi * (n + offset) / (N)) for n in range(N)])


def make_filter(offset, num_taps):
    return array([sinc(n + offset - num_taps / 2) for n in range(num_taps)]) * \
        hamming_offset(num_taps, offset=offset)


def make_filter_bank(steps, num_taps):
    return [
        make_filter(1 - n / (steps - 1), num_taps)
        for n in range(steps)
    ]


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-n", "--num-taps",
        type=int,
        default=8,
    )
    parser.add_argument(
        "-s", "--steps",
        type=int,
        default=128,
    )
    parser.add_argument(
        "-f", "--format",
        choices=["csv", "c++"],
        default="csv"
    )
    parser.add_argument(
        "-o", "--output",
        type=pathlib.Path,
        default=pathlib.Path("/dev/stdout"),
    )
    args = parser.parse_args()

    bank = make_filter_bank(args.steps, args.num_taps)

    with args.output.open("w") as f:
        if args.format == "csv":
            writer = csv.writer(f)
            writer.writerows(bank)
        elif args.format == "c++":
            def push(ls):
                f.write("\n".join(ls))
                f.write("\n")
            push([
                "// Generated by kvak/interpol.py",
                "",
                "const unsigned int num_taps = {};".format(args.num_taps),
                "const unsigned int num_steps = {};".format(len(bank)),
                "", "",
                "constexpr float filter_coeffs[num_steps][num_taps] = {",
            ])
            for i, filt in enumerate(bank):
                f.write(
                    "\t{ " + ", ".join(map(str, filt)) + " }},  // {} / {}\n"
                    .format(i, len(bank) - 1)
                )
            push([
                "};",
                "",
            ])

#! /usr/bin/env python3 # This is using the window method from # http://users.spa.aalto.fi/vpv/publications/vesan_vaitos/ch3_pt1_fir.pdf # Not the best method (worse than what gnuradio does anyway), but let's not care # about that for now. # Notice the division by N instead of N - 1, that is intentional so that we # don't wrap to the next cosine period