sergeyhaki / ForChat

0 stars 0 forks source link

Example #1

Open sergeyhaki opened 1 year ago

sergeyhaki commented 1 year ago

/ To implement a 3rd order all-pass filter on an AVR MCU using int32_t data types, you can use the following code: /

include

define SCALE_FACTOR 32768

int32_t allPassFilter3rdOrder(int32_t input, int32_t* delayLine) { int32_t output;

// Coefficients for a 3rd order all-pass filter
const int32_t b0 = SCALE_FACTOR;
const int32_t b1 = -2 * SCALE_FACTOR;
const int32_t b2 = SCALE_FACTOR;
const int32_t a1 = -2 * SCALE_FACTOR;
const int32_t a2 = SCALE_FACTOR;

// Calculate the output
output = ((b0 * input) + (b1 * delayLine[0]) + (b2 * delayLine[1])) / SCALE_FACTOR;
output -= (a1 * delayLine[0] + a2 * delayLine[1]) / SCALE_FACTOR;

// Update the delay line
delayLine[1] = delayLine[0];
delayLine[0] = output;

return output;

}

/ This function takes an input sample as an int32_t and a pointer to a delay line array, which should have a length of 2. The function returns the filtered output as an int32_t.The coefficients for a 3rd order all-pass filter are defined as constant variables using the SCALE_FACTOR of 32768. The output is calculated using these coefficients and the input sample, as well as the current and previous values in the delay line buffer.To use this function, you would call it in your main program loop, passing in the input sample and the delay line array. The filter will update the delay line with the new output value and return the filtered output. /

sergeyhaki commented 1 year ago

The Hilbert Transform is a digital signal processing algorithm that can be used to generate an analytic signal, which has a complex envelope that can be used for various signal processing tasks. Here is a sample implementation of the discrete-time Hilbert transform for AVR MCU (ATmega328P) using  int16_t  data type:

 

include <avr/io.h>

include // for the sin() function

// parameters

define N 64 // number of samples

define PI 3.14159265358979323846

int16_t signal[N]; // input signal int16_t envelope[N]; // envelope of the signal using Hilbert transform

int main(void) { // generate input signal for (int i = 0; i < N; i++) { signal[i] = 1000 sin(2 PI * i / N); }

// compute Hilbert transform
int16_t hilbert[N];
for (int n = 0; n < N; n++) {
    hilbert[n] = 0;
    for (int k = 0; k < N; k++) {
        hilbert[n] += signal[k] * sin(2 * PI * (n - k) / N) * (2.0 / N);
    }
}

// compute envelope
for (int i = 0; i < N; i++) {
    envelope[i] = sqrt(signal[i] * signal[i] + hilbert[i] * hilbert[i]);
}

// do something with the envelope

return 0;

}  

The program starts by generating a test input signal that consists of a 1kHz sine wave with a peak amplitude of 1000. The signal is stored in an array  signal . The program then computes the Hilbert transform by iterating over each sample  n  of the output signal and summing up the contributions from all samples  k  of the input signal. The formula used is:

  H[n] = 1/N sum_{k=0}^{N-1} x[k] sin(2 pi (n-k) / N)  

Where  x  is the input signal. The result is stored in an array  hilbert .

The program then computes the envelope of the signal by taking the magnitude of the complex analytic signal represented by  signal  and  hilbert . The formula used is:

  env[n] = sqrt(x[n]^2 + H[n]^2)  

The result is stored in an array  envelope .

Finally, the program can do something with the envelope, such as display it on a display or use it for further processing. Note that this implementation is just a sample and may not be optimal or suited for all applications.

sergeyhaki commented 1 year ago

Вот пример реализации All-Pass фильтра 3 порядка на языке C для AVR, используя тип данных int32_t для входного, выходного сигнала и коэффициентов фильтра:

#define N 3
#define Q 31

// Коэффициенты фильтра для частоты 1 рад/отсек, H(z) = (b0 + b1*z^-1 + b2*z^-2) / (1 + a1*z^-1 + a2*z^-2)
int32_t a[N] = {Q, -Q/3, Q/2}; // a1=0.33, a2=-0.5
int32_t b[N] = {Q/3, -Q/2, Q}; // b0=0.33, b1=-0.5, b2=1

int32_t apply_allpass_filter(int32_t input)
{
    static int32_t x[N+1] = {0}; // Входные значения x[n], индекс от 0 до N
    static int32_t y[N+1] = {0}; // Выходные значения y[n], индекс от 0 до N

    int32_t output = (b[0] * input) >> Q; // Для возможности использования смены знака Q

    // Обновляем входные и выходные буферы
    for (int i = N; i > 0; i--) {
        x[i] = x[i-1];
        y[i] = y[i-1];
    }
    x[0] = input;

    // Рассчитываем выходное значение
    for (int i = 1; i <= N; i++) {
        output += ((b[i] * x[i]) >> Q) - ((a[i-1] * y[i]) >> Q);
    }
    y[0] = output;

    return output;
}

int main(void)
{
    while (1) {
        // Считываем входное значение и применяем all-pass фильтр
        int32_t input = get_input();
        int32_t output = apply_allpass_filter(input);

        // Микроконтроллер AVR не поддерживает тип double и float, поэтому не нужно преобразовывать тип данных
        // Делаем что-то с выходным значением
        do_something(output);
    }
    return 0;
}

Здесь Q - это коэффициент смены знака, равный 2^31. Это используется для уменьшения ошибок округления при применении коэффициентов фильтра в формулах.

Обратите внимание, что в этой реализации входные и выходные значения фильтра хранятся в целочисленных типах данных с 32-битной длиной. Это позволяет ускорить вычисления и сэкономить память, по сравнению с использованием типов double и float.

sergeyhaki commented 1 year ago

Конечно! Вот пример кода на языке C для Atmega328pb, который настраивает вывод OC1A (нога 15 на Atmega328pb) на производство прямоугольного сигнала PWM с одинаковым временем включения и выключения для обоих состояний. Программа также настраивает вывод OC1B (нога 16 на Atmega328pb) для производства инвертированного PWM сигнала с тем же временем включения и выключения.#include <avr/io.h>

define PWM_FREQ 1000 // Частота PWM

void pwm_init(void) { // Настройка ноги PD5 (OC1A) для прямого PWM DDRD |= (1<<PD5); // Настройка PD5 на выход TCCR1A |= (1<<COM1A1) | (1<<WGM11); // Настройка режима 14 (fast PWM от ICR1), и установка ситгнала OC1A на non-inverting (COM1A1=1) TCCR1B |= (1<<WGM13) | (1<<WGM12) | (1<<CS10); // Настройка режима 14 (fast PWM от ICR1), установка prescaler на 1 (CS10=1) ICR1 = F_CPU/(2*PWM_FREQ); // Настройка значения регистра ICR1 для заданной частоты PWM

// Настройка ноги PD4 (OC1B) для инвертированного PWM
DDRD |= (1<<PD4); // Настройка PD4 на выход
TCCR1A |= (1<<COM1B1); // Установка ситгнала OC1B на inverted (COM1B1=1)

}

int main(void) { pwm_init(); // Инициализация PWM

while (1)
{
    // Здесь можно изменить значение скважности пр
sergeyhaki commented 1 year ago

Конечно, вот переписанный пример программы на языке C, используя целочисленные значения:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define ARRAY_SIZE 100   // Размер массива
#define MEAN 0           // Среднее значение нормального распределения
#define STD_DEV 1        // Стандартное отклонение нормального распределения

int main() {
    int array[ARRAY_SIZE];
    int max_value;
    int max_index;

    // Генерация случайных чисел с нормальным распределением
    srand(time(NULL));
    for (int i = 0; i < ARRAY_SIZE; i++) {
        double u1 = (double)rand() / RAND_MAX;
        double u2 = (double)rand() / RAND_MAX;
        double z = sqrt(-2.0 * log(u1)) * cos(2.0 * M_PI * u2);
        array[i] = MEAN + STD_DEV * z;
    }

    // Нахождение максимального значения
    max_value = array[0];
    max_index = 0;
    for (int i = 1; i < ARRAY_SIZE; i++) {
        if (array[i] > max_value) {
            max_value = array[i];
            max_index = i;
        }
    }

    // Вычисление 2 значений в 2 раза меньше максимального (слева и справа)
    int left_value = max_value / 2;
    int right_value = max_value / 2;
    if (max_index > 0) {
        left_value = array[max_index - 1];
    }
    if (max_index < ARRAY_SIZE - 1) {
        right_value = array[max_index + 1];
    }

    // Вывод результатов
    printf("Массив с нормальным распределением:\n");
    for (int i = 0; i < ARRAY_SIZE; i++) {
        printf("%d ", array[i]);
    }
    printf("\n\n");
    printf("Максимальное значение: %d\n", max_value);
    printf("Левое значение в 2 раза меньше максимального: %d\n", left_value);
    printf("Правое значение в 2 раза меньше максимального: %d\n", right_value);

    return 0;
}

Здесь производится генерация случайных целочисленных значений с нормальным распределением в массиве array размером ARRAY_SIZE. Затем ищется максимальное значение и его индекс в массиве. Далее вычисляются значения слева и справа от максимального значения, удовлетворяющие условию в два раза меньше максимального. Результаты выводятся на экран.

Обратите внимание, что в данном случае используется функция printf для вывода целочисленных значений. Если вы хотите вывести значения с плавающей точкой с некоторым форматированием, вам потребуется использовать функцию sprintf или подобную функцию для преобразования чисел в строку и вывод строки.