import numpy as np
from typing import List, Union, Tuple
from resistics.common.base import ResisticsBase
from resistics.config.io import loadConfig
from resistics.decimate.evalfreqs import getEvaluationFreqSize
from resistics.common.print import (
generalPrint,
warningPrint,
errorPrint,
blockPrint,
arrayToString,
)
[docs]class DecimationParameters(ResisticsBase):
"""Class to calculate and hold decimation parameters
Attributes
----------
sampleFreq : float
Sampling frequency in Hz.
divFactor : int
Minimum division factor when working out decimation factors
numLevels : int
Number of decimation levels
freqPerLevel : int
Number of frequencies per decimation level
decFactors : np.ndarray
Incremental decimation factors
decFrequencies : np.ndarray
Sampling frequencies at each decimation level
evalFreq : np.ndarray
Array of evaluation frequencies
evalFreqPerLevel : np.ndarray
2-D array (numLevels,freqPerLevel) of evaluation frequencies
Methods
-------
__init__(sampleFreq)
Initialise with sampling frequency
getSampleFreqLevel(decLevel)
Get the sample frequency of the level
getDecFactor(decLevel)
Get the decimation factor from level 0 to decLevel
getIncrementalFactor(decLevel)
Get the decimation factor from decLevel-1 to decLevel
getEvalFrequenciesForLevel(decLevel)
Returns evaluation frequencies for decLevel
setFrequencyParams(evalFreq, freqPerLevel, maxLevel)
Set decimation parameters by evaluation frequencies and frequencies per level
setDecimationParams(numLevels, freqPerLevel)
Set decimation parameters by number of levels and frequencies per level
calcFrequencyParams(numLevels, freqPerLevel)
Calculate decimation parameters based on number of levels and frequencies per level
calcDecimationParams(evalFreq, maxLevel, freqPerLevel)
Calculate decimation paratmers based on evaluation frequencies, frequecies per level and max level
calcNearestFactor(freq)
Calculate nearest decimation factor
printList()
Class status returned as list of strings
"""
def __init__(self, sampleFreq: float):
"""Initialise decimation parameters with sampling frequency
Calculates decimation factors and evaluation frequencies based on defaults
Parameters
----------
sampleFreq : float
Sampling frequency
"""
self.sampleFreq = sampleFreq
config = loadConfig()
self.divFactor: int = 2
self.numLevels: int = config["Decimation"]["numlevels"]
self.freqPerLevel: int = config["Frequencies"]["perlevel"]
self.decFactors: Union[np.ndarray, None] = None
self.decFrequencies: Union[np.ndarray, None] = None
self.evalFreq: Union[np.ndarray, None] = None
self.evalFreqPerLevel: Union[np.ndarray, None] = None
# calculate some initial values decimation parameters based on defaults
self.calcFrequencyParams(self.numLevels, self.freqPerLevel)
[docs] def getSampleFreqLevel(self, decLevel: int) -> float:
"""Get sampling frequency for decimation level
Parameters
----------
decLevel : int
Decimation level
Returns
-------
out : float
Sample frequency for decimation level decLevel
"""
self.checkDecimationLevel(decLevel)
return self.sampleFreq / self.getDecFactor(decLevel)
[docs] def getDecFactor(self, decLevel: int) -> float:
"""Get decimation factor for decimation level
Returns decimation factor relative to level 0
Parameters
----------
decLevel : int
Decimation level
Returns
-------
out : float
Decimation factor for level decLevel
"""
self.checkDecimationLevel(decLevel)
return self.decFactors[decLevel]
[docs] def getIncrementalFactor(self, decLevel: int) -> int:
"""Get decimation factor for decimation level
Returns decimation factor relative to decLevel - 1
Parameters
----------
decLevel : int
Decimation level
Returns
-------
out : int
Incremental decimation factor from decLevel - 1
"""
self.checkDecimationLevel(decLevel)
if decLevel == 0:
return int(self.decFactors[decLevel])
else:
return int(self.decFactors[decLevel] / self.decFactors[decLevel - 1])
[docs] def getEvalFrequenciesForLevel(self, decLevel: int) -> np.ndarray:
"""Get evaluation frequencies for level decLevel
Parameters
----------
decLevel : int
Decimation level
Returns
-------
out : np.ndarray
Array of evaluation frequencies for level decLevel
"""
self.checkDecimationLevel(decLevel)
return self.evalFreqPerLevel[decLevel, :]
[docs] def checkDecimationLevel(self, decLevel: int) -> None:
"""Checks user provided decimation levels and quits if out of bounds
Parameters
----------
decLevel : int
Decimation level
"""
if decLevel < 0:
self.printError("Decimation level must be greater than 0", quitRun=True)
if decLevel > self.numLevels - 1:
self.printError(
"Decimation level must be less than or equal to {}".format(
self.numLevels - 1
),
quitRun=True,
)
[docs] def setFrequencyParams(
self, evalFreq: List, freqPerLevel: int, maxLevel: int
) -> None:
"""Sets frequency parameters and calculates decimation paramters based on those
Parameters
----------
evalFreq : List
Evaluation frequencies
freqPerLevel : int
Number of evaluation frequencies per level
maxLevel : int
Maximum decimation level
"""
evalFreq[::-1].sort() # sort in descending order
self.calcDecimationParams(evalFreq, freqPerLevel, maxLevel)
[docs] def setDecimationParams(self, numLevels: int, freqPerLevel: int) -> None:
"""Sets number of levels and frequencies per levels to calculate decimation parameters
Parameters
----------
numLevels : int
Number of decimation levels
freqPerLevel : int
Number of evaluation frequencies per level
"""
self.calcFrequencyParams(numLevels, freqPerLevel)
[docs] def calcFrequencyParams(self, numLevels: int, freqPerLevel: int) -> None:
"""Calculates evaluation frequencies and decimation parameters
Uses number of levels and frequencies per levels to auto-calculate evaluation frequencies and then the decimation parameters.
Parameters
----------
numLevels : int
Number of decimation levels
freqPerLevel : int
Number of evaluation frequencies per level
"""
numFreq = numLevels * freqPerLevel
evalFreq = getEvaluationFreqSize(self.sampleFreq, numFreq)
self.calcDecimationParams(evalFreq, numLevels, freqPerLevel)
[docs] def calcDecimationParams(
self, evalFreq: Union[np.ndarray, List], maxLevel: int, freqPerLevel: int
) -> None:
"""Calculate decimation parameters from evaluation frequencies
Uses evaluation frequencies, number of frequencies per decimation level and max allowable decimation level to calculate decimation parameters.
Parameters
----------
evalFreq : List, np.ndarray
Number of decimation levels
maxLevel : int
Maximum allowable number of decimation levels
freqPerLevel :
Number of frequencies per level
"""
evalFreq = np.array(evalFreq)
# calculating decimation parameters from evaluation frequencies
maxf = self.sampleFreq / 4
# find the first evaluation frequency less than or equal to maxf
fHigh = evalFreq[0]
for ifreq in range(0, evalFreq.size):
if evalFreq[ifreq] <= maxf:
fHigh = evalFreq[ifreq]
break
iHigh = evalFreq.tolist().index(fHigh)
evalFreqSub = evalFreq[iHigh:]
# calculate number of levels
numLevels = maxLevel
# check if enough evaluation frequencies
if len(evalFreqSub) < freqPerLevel * maxLevel:
# numLevels = int(math.floor(len(evalFreqSub)/freqPerLevel))
numLevels = int(np.ceil(1.0 * len(evalFreqSub) / freqPerLevel))
# do another subslice
evalFreqSub = evalFreqSub[: numLevels * freqPerLevel]
# now create an array of evalation frequencies per decimation level
# evalFreqPerLevel = np.ones(shape=(numLevels, freqPerLevel))
evalFreqPerLevel = np.ones(shape=(numLevels, freqPerLevel)) * -1
for ilevel in range(0, numLevels):
for ifreq in range(0, freqPerLevel):
if ilevel * freqPerLevel + ifreq >= len(evalFreqSub):
break
evalFreqPerLevel[ilevel, ifreq] = evalFreqSub[
ilevel * freqPerLevel + ifreq
]
# now calculate decimation factors
decFactors = np.ones(shape=(numLevels))
decFrequencies = np.ones(shape=(numLevels))
for ilevel in range(0, numLevels):
decFactors[ilevel], decFrequencies[ilevel] = self.calcNearestFactor(
evalFreqPerLevel[ilevel][0]
)
# finally, set all parameters
self.evalFreq = evalFreqSub
self.freqPerLevel = freqPerLevel
self.numLevels = numLevels
self.evalFreqPerLevel = evalFreqPerLevel
self.decFactors = decFactors
self.decFrequencies = decFrequencies
[docs] def calcNearestFactor(self, freq: float):
"""Calculates nearest decimation factor given frequency
Parameters
----------
freq : float
Frequency for which to calculate decimation factor
Returns
-------
fac : int
The decimation factor
f : float
Sampling frequency in Hz given the decimation factor (sampleFreq/decimationFactor)
"""
# want sampling frequency to be 4 times greater than highest freq
fsMin = freq * 4
# set to initial sampling frequency
f = float(self.sampleFreq)
fac = 1
while f > fsMin * self.divFactor:
f = f / self.divFactor
fac = fac * self.divFactor
return fac, f
[docs] def printList(self) -> List[str]:
"""Class information as a list of strings
Returns
-------
out : list
List of strings with information
"""
textLst = []
textLst.append("Sampling frequency = {:f}".format(self.sampleFreq))
textLst.append("Number of decimation levels = {:d}".format(self.numLevels))
# decimation factors
for il in range(0, self.numLevels):
textLst.append(
"Level = {:d}\tsample freq. [Hz] = {:.6f}\tsample rate [s] = {:.6f}\tdec. factor = {:07d}\tinc. factor = {:d}".format(
il,
self.decFrequencies[il],
1.0 / self.decFrequencies[il],
int(self.decFactors[il]),
self.getIncrementalFactor(il),
)
)
# evaluation frequencies
textLst.append("Evaluation frequencies [Hz]")
for il in range(0, self.numLevels):
freqForLevel = self.getEvalFrequenciesForLevel(il)
eFreqStr = arrayToString(freqForLevel)
textLst.append("Level = {:d}: {}".format(il, eFreqStr))
return textLst