Генерація таблиці синусів
Знадобилася таблична генерація синусоїдальних сигналів — звичайнісінький функціональний генератор. Накопичувач фази та вибірка готового значення з таблиці. Не я перший, не я останній.
Цікаво, який метод отримання таблиці синусів для найпоширеніший?
Пошук в інтернеті готової таблиці з потрібними параметрами (розрядність даних, амплітуда, кількість квадрантів у таблиці та точок)?
Розрахунки у електронній таблиці (OpenOffice calc чи там MS Excel з експортом у .csv та переносом результатів у C-файл?
Не виключаю, що при певних навичках користування гуглом перший варіант дасть найшвидший результат 🙂
Але це не наш метод. Будь-яка зміна параметрів — і заново шукай чи міняй таблицю. Мені ж зазвичай хочеться автоматизувати процеси. Зрештою, «machines should work, people should think».
Як це часто буває, хтось вже все придумав, лише бери і користуйся.
Є такий чудовий генератор cog. Він шукає у вхідному файлі (написаному не обов’язково мовами С/С++) коментарі спеціальної форми, в яких розміщено пітон-скрипти, і породжує вихідний файл, у якому поруч з початковим текстом додається згенерований фрагмент коду. Тобто можна доповнити написаний код фрагментами, згенерованими автоматично. До генерації власне коду я не дійшов, а от таблички іноді генерую.
Якось так (файл sin_table.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
. До переліку об’єктних файлів додамо файл з таблицею:
Десь після правила для генерації .o
-файлів з .cpp
додамо правило для генерації .cpp
з .cog
.
%.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
лежить.