Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
utilities.py 3.12 KiB
#! /usr/bin/env python3
###############################################################################
#
# Copyright 2024
# ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

###############################################################################

# Author: Eric Kooistra
# Purpose: Utilities and functions for DSP
# Description:
#
# References:
# [1] dsp_study_erko.txt

import numpy as np

c_atol = 1e-15
c_rtol = 1e-8   # 1/2**32 = 2.3e-10


###############################################################################
# Utilities
###############################################################################

def ceil_div(num, den):
    """ Return integer ceil value of num / den """
    return int(np.ceil(num / den))


def ceil_log2(num):
    """ Return integer ceil value of log2(num) """
    return int(np.ceil(np.log2(num)))


def ceil_pow2(num):
    """ Return power of 2 value that is equal or greater than num """
    return 2**ceil_log2(num)


def pow_db(volts):
    """Voltage to power in dB"""
    return 20 * np.log10(np.abs(volts) + c_atol)


def is_even(n):
    """Return True if n is even, else False when odd.

    For all n in Z.
    """
    return n % 2 == 0


def is_odd(n):
    """Return True if n is odd, else False when even.

    For all n in Z, because result of n % +2 is 0 or +1.
    """
    return n % 2 == 1


def is_symmetrical(x, anti=False):
    """Return True when x[n] = +-x[N-1 - n], within tolerances, else False."""
    rtol = c_rtol
    atol = np.min(np.abs(x[np.nonzero(x)])) * rtol
    n = len(x)
    h = n // 2
    if is_even(n):
        if anti:
            return np.allclose(x[0:h], np.flip(-x[h:]), rtol=rtol, atol=atol)
        else:
            return np.allclose(x[0:h], np.flip(x[h:]), rtol=rtol, atol=atol)
    else:
        if anti:
            return np.allclose(x[0:h], np.flip(-x[h + 1:]), rtol=rtol, atol=atol) and np.abs(x[h]) < atol
        else:
            return np.allclose(x[0:h], np.flip(x[h + 1:]), rtol=rtol, atol=atol)


def read_coefficients_file(filepathname):
    coefs = []
    with open(filepathname, 'r') as fp:
        for line in fp:
            if line.strip():  # skip empty line
                s = int(line)   # one coef per line
                coefs.append(s)
    return coefs


def one_bit_quantizer(x):
    """Quantize 0 and positive x to +1 and negative x to -1."""
    return np.signbit(x) * -1 + 2


def impulse_at_zero_crossing(x):
    """Create signed impulse at zero crossings of x."""
    diff = np.diff(one_bit_quantizer(x))
    return np.concatenate((np.array([0]), diff))