Генерація таблиці синусів

Дивно, я чомусь думав, що про cog я вже писав.

Знадобилася таблична генерація синусоїдальних сигналів — звичайнісінький функціональний генератор. Накопичувач фази та вибірка готового значення з таблиці. Не я перший, не я останній.

Цікаво, який метод отримання таблиці синусів для найпоширеніший?
Пошук в інтернеті готової таблиці з потрібними параметрами (розрядність даних, амплітуда, кількість квадрантів у таблиці та точок)?
Розрахунки у електронній таблиці (OpenOffice calc чи там MS Excel з експортом у .csv та переносом результатів у C-файл?

Не виключаю, що при певних навичках користування гуглом перший варіант дасть найшвидший результат 🙂
Але це не наш метод. Будь-яка зміна параметрів — і заново шукай чи міняй таблицю. Мені ж зазвичай хочеться автоматизувати процеси. Зрештою, «machines should work, people should think».

Як це часто буває, хтось вже все придумав, лише бери і користуйся.
Є такий чудовий генератор cog. Він шукає у вхідному файлі (написаному не обов’язково мовами С/С++) коментарі спеціальної форми, в яких розміщено пітон-скрипти, і породжує вихідний файл, у якому поруч з початковим текстом додається згенерований фрагмент коду. Тобто можна доповнити написаний код фрагментами, згенерованими автоматично. До генерації власне коду я не дійшов, а от таблички іноді генерую.

Якось так (файл sin_table.cog):

/*[[[cog

import math
import os.path

phase_bits = 5
amplitude = 2047

points_per_row = 8

phase_div = 1 << phase_bits
sin_table_size = phase_div + 1

title = """//--------------------------------------------------------------
//  sin table
//
// DO NOT EDIT
//    this file is generated from %s
//
"""
% cog.inFile

header_file_name = os.path.splitext(cog.outFile)[0] + '.h'
hheader = open(header_file_name, 'w')
hheader.writelines(
   (title,
   '\n#pragma once\n#include <stdint.h>\n',
   'int const sin_phase_bits = %d;\n' % phase_bits,
   'int const sin_table_size = %d;\n' % sin_table_size,
   'extern uint16_t const sin_table[sin_table_size];\n'
   )
)
hheader.close()

cog.outl(title)
cog.outl('#include "' + header_file_name + '"')
cog.outl('uint16_t const sin_table[sin_table_size] = {')

for i in range(0,sin_table_size) :
    if (i % points_per_row) == 0 : cog.out('   ')
    cog.out(' %d,' % int(amplitude * math.sin(math.pi/2 * i / phase_div) + 0.5) )
    if (i % points_per_row) == (points_per_row-1) : cog.out('\n')

cog.outl('\n}; // sin_table[]\n')

]]]*/
//[[[end]]]

Запуск конвертора автоматизуємо відповідним правилом у makefile. До переліку об’єктних файлів додамо файл з таблицею:

    OBJS    += sin_table.o

Десь після правила для генерації .o-файлів з .cpp додамо правило для генерації .cpp з .cog.

    COG     = cog.py

%.cpp : %.cog
    echo --- make $@ from $<
    $(COG) -d $< >$@

Явно прописувати необхідність генерації sin_table.cpp потреби нема — користуючись цими правилами утиліта make сама знайде ланцюжок для створення sin_table.o з sin_table.cog.

Результатом оброблення вказаного вище sin_table.cog буде два файли.
sin_table.h

//--------------------------------------------------------------
//  sin table
//
// DO NOT EDIT
//    this file is generated from sin_table.cog
//

#pragma once
#include <stdint.h>
int const sin_phase_bits = 5;
int const sin_table_size = 33;
extern uint16_t const sin_table[sin_table_size];

sin_table.cpp

//--------------------------------------------------------------
//  sin table
//
// DO NOT EDIT
//    this file is generated from sin_table.cog
//

#include "sin_table.h"
uint16_t const sin_table[sin_table_size] = {
    0, 100, 201, 300, 399, 497, 594, 690,
    783, 875, 965, 1052, 1137, 1219, 1299, 1375,
    1447, 1517, 1582, 1644, 1702, 1756, 1805, 1850,
    1891, 1927, 1959, 1986, 2008, 2025, 2037, 2045,
    2047,
}; // sin_table[]

Тепер для зміни параметрів слід лише поміняти значення змінних phase_bits та amplitude на початку скри́пту і make сам перегенерує проміжні файли і перезбере проект.

p.s. У конкретному випадкові таблиці синусів .cog-файл вийшов виродженим, в ньому знаходиться лише коментар зі скриптом. Можна було б обійтися без cog, використати звичайний скрипт на пітоні. Але руки пішли звичним шляхом та й для cog вже готове правило у типовому makefile лежить.

Leave a Reply

[flagcounter image]