40 lines
6.6 KiB
Python
40 lines
6.6 KiB
Python
"""Потоковый CIC-декодер однобитного сигма-дельта модулятора без зависимостей от GUI.""" # Ядро одинаково работает в DSView и автономных анализаторах.
|
|
|
|
|
|
class SigmaDeltaFilter: # Каскад интеграторов и гребенчатых звеньев реализует фильтры sinc первого, второго и третьего порядка.
|
|
def __init__(self, decimation=256, order=3, invert=False): # Децимация задаётся числом тактов на результат; 256 и sinc3 являются начальными настройками анализатора.
|
|
if isinstance(decimation, bool) or not isinstance(decimation, int) or not 1 <= decimation <= 65536: # Ограничение 65536 тактов удерживает максимальное усиление sinc3 в пределах 49 бит.
|
|
raise ValueError('Decimation must be an integer from 1 to 65536.') # Дробные и неположительные окна не определяют допустимую децимацию.
|
|
if isinstance(order, bool) or not isinstance(order, int) or order not in (1, 2, 3): # Число интеграторов должно совпадать с числом гребенчатых звеньев.
|
|
raise ValueError('Filter order must be 1, 2 or 3.') # Неизвестный порядок нельзя незаметно заменять sinc3.
|
|
self.decimation = decimation # Каждый результат появляется после этого числа принятых битов.
|
|
self.order = order # Порядок также определяет длительность начального установления фильтра.
|
|
self.invert = bool(invert) # Инверсия применяется к битам до фильтрации и меняет знак восстановленного сигнала.
|
|
self.gain = decimation ** order # Усиление CIC при постоянном входе равно R в степени порядка при дифференциальной задержке один.
|
|
self.mask = (1 << self.gain.bit_length()) - 1 # Модуль степени двойки строго больше усиления сохраняет точный результат при переполнениях интеграторов.
|
|
self.integrators = [0] * order # Нулевое начальное состояние не предполагает неизвестную предысторию захвата.
|
|
self.delays = [0] * order # Каждый гребенчатый каскад хранит свой предыдущий вход на пониженной частоте.
|
|
self.phase = 0 # Счётчик внутри окна исключает накопление дробной ошибки децимации.
|
|
self.blocks = 0 # Первые order минус один результатов относятся к установлению и не выдаются пользователю.
|
|
|
|
def feed(self, bit): # Принимается ровно один уровень DATA на выбранном фронте CLK.
|
|
if bit not in (0, 1): # Служебные значения неподключённых каналов sigrok не являются цифровыми уровнями.
|
|
raise ValueError('Sigma-delta data must be a logic level 0 or 1.') # Ошибка назначения входов не должна превращаться в правдоподобную амплитуду.
|
|
value = int(bit) ^ self.invert # Униполярное представление позволяет нормировать результат без знакового переполнения.
|
|
for stage in range(self.order): # Последовательно обновляем интеграторы на полной частоте модулятора.
|
|
self.integrators[stage] = (self.integrators[stage] + value) & self.mask # Модульная арифметика ограничивает память независимо от длительности записи.
|
|
value = self.integrators[stage] # Следующий интегратор получает уже обновлённый выход предыдущего.
|
|
self.phase += 1 # Считаем только фактически захваченные тактовые фронты.
|
|
if self.phase < self.decimation: # Между границами децимации выходной отсчёт ещё не готов.
|
|
return None # Неполные окна не дополняются вымышленными битами.
|
|
self.phase = 0 # Новый блок начинается со следующего реального бита.
|
|
for stage in range(self.order): # Гребенчатые звенья выполняются только на выходной частоте.
|
|
previous = value # Задержка сохраняет вход текущего звена, а не его разность.
|
|
value = (value - self.delays[stage]) & self.mask # Разности устраняют накопление интеграторов, включая их модульные переполнения.
|
|
self.delays[stage] = previous # Это состояние используется через R входных тактов.
|
|
self.blocks = min(self.order, self.blocks + 1) # Счётчик установления насыщается и не растёт вместе с записью.
|
|
if self.blocks < self.order: # Для sincN пропускаем первые N минус один выходных блоков с неизвестной предысторией.
|
|
return None # Частично установившийся фильтр не выдаёт ложное смещение постоянного сигнала.
|
|
code = 2 * value - self.gain # Биполярный код принимает диапазон от минус R**N до плюс R**N без привязки к конкретному АЦП.
|
|
return {'code': code, 'gain': self.gain, 'value': code / self.gain, 'density': value / self.gain} # Нормированная амплитуда и фильтрованная плотность единиц сохраняют точный масштаб CIC.
|