цифровая электроника
вычислительная техника
встраиваемые системы

 


Анализ взаимной корреляции сигналов с помощью Arduino

Автор: Mike(admin) от 17-06-2021, 07:55

Когда вам нужно сравнить два сдвинутых во времени сигнала, взаимная корреляция – один из лучших способов измерения фазы.


Анализ взаимной корреляции сигналов с помощью Arduino

В некоторых случаях необходимо знать, насколько аналоговые сигналы сдвинуты во времени относительно друг друга, независимо от их амплитуды. Стандартный метод получения результата называется взаимной корреляцией, и он появился примерно в 1920 году.


Например, его можно использовать для обобщенной кросс-корреляции с фазовым преобразованием (GCC-PHAT) для вычисления временной задержки между двумя или более каналами. При небольшом объеме памяти Arduino это можно сделать только с определенными ограничениями, и функционирование очень сильно зависит от частот, которые фактически содержат сигналы. В случае синусоидальных сигналов данный метод на Arduino работает неплохо.


Анализ взаимной корреляции сигналов с помощью Arduino

Аналоговые входные сигналы могут приниматься либо от двух микрофонов, расположенных на определенном расстоянии друг от друга, либо от генератора синусоидальных сигналов, один непосредственно, а другой с задержкой на высоких или низких частотах. В этом случае не забудьте сместить уровень постоянного тока на диапазон от нуля до пяти вольт.


На приведенном выше рисунке показан плоттер последовательного монитора, который отображает два сигнала, подаваемые на аналоговые входы (синий и красный). Ниже вы видите функцию взаимной корреляции (оранжевый), отметки и центр выделенного интервала (темно-зеленый) и прямоугольник (фиолетовый), указывающий, где находится максимальное значение функции взаимной корреляции. Действителен только крайний правый прямоугольник; с ограниченным объемом оперативной памяти невозможно подавить предыдущие прямоугольники. Все это делается за один раз с помощью вложенного цикла.



for (int i = 0; i < N; i++) {
    ...
    long cc = 0;
    for (int j = 0, k = i - N2; j < N; j++, k++)
      if (k >= 0 && k < N) cc = cc + (long) a[j] * b[k];
    ...
}

Перед этим выборки нужно прочитать в два массива a и b. Поскольку последовательный плоттер всегда показывает 500 выборок, то же самое значение было выбрано для размерности целочисленных массивов. Итак, из 2048 байтов СОЗУ только 48 байтов остаются свободными для некоторых переменных и стека. В данном случае не было места для занимающего много места класса Serial, поэтому без всякой роскоши реализовывались только элементарные функции для вывода. Используя класс Serial, вы можете обрабатывать до 420 выборок. В итоге не осталось ни одного байта.


Существует возможность установить скорость выборки, установив перемычку между двух соседних контактах в области контактов 2 - 7, которая устанавливает предварительный делитель АЦП на значения от 2 до 128, что также влияет на точность преобразования АЦП (setAdcPrescaler()).


Программа показывает отметки, центр и положение максимума. Очевидно, только один максимум может быть правильно обнаружен.


Анализ взаимной корреляции сигналов с помощью Arduino

Фазовый сдвиг между двумя сигналами будет разницей между центром и положением максимума. К сожалению, когда вы подаете на устройство сложные сигналы, в большинстве случаев правильные значения фазы не будут видны.


Далее приведен код взаимной корреляции двух низкочастотных сигналов. Просто подайте два сигнала на аналоговые входы A0 и A5 и посмотрите, как отображается функция взаимной корреляции.



#include "Tx.h" // replacement for Serial class

void setup() {
  Seriell_begin(115200);
  cr();
}

const int N = 500;
const int N2 = N / 2;
int a[N], b[N]; 

void loop() {
  setAdcPrescaler();
  for (int i = 0; i < N; i++) {
    a[i] = analogRead(A0);
    b[i] = analogRead(A5);
  }
  // calc mean:
  long ma = 0;
  long mb = 0;
  for (int i = 0; i < N; i++) {
    ma = ma + a[i];
    mb = mb + b[i];
  }
  ma = ma / N;
  mb = mb / N;
  int mc = -1;
  for (int i = 0; i < N; i++) {
    a[i] = a[i] - ma;
    mc = max(mc, a[i]);
    b[i] = b[i] - mb;
    mc = max(mc, b[i]);
  }
  const int tl = 800;
  const long amp = 200;
  const int n10 = N / 10; 
  byte i10 = 0;
  long maxNew = -1E8;
  for (int i = 0; i < N; i++) {
    tx_N(tl + amp * a[i] / mc);
    tab();
    tx_N(tl + amp * b[i] / mc);
    tab();
    tx_N(tl);
    tab();
    long cc = 0;
    for (int j = 0, k = i - N2; j < N; j++, k++)
      if (k >= 0 && k < N) cc = cc + (long) a[j] * b[k];
    tx_N(4 * cc / mc / mc);
    // -----------------------------------
    tab();
    if (cc > maxNew) {
      maxNew = cc;
      //phase = i - N2;
      tx_N(0);
    }
    else tx_N(200);
    tab();
    if (i == N2) tx_N(-250);
    else if (i + 1 > i10 * n10) {
      tx_N(-50);
      i10++;
    }
    else tx_N(0);
    cr();
  }
  delay(1000);
}

void setAdcPrescaler() {
  for (byte i = 2; i < 8; i++) {
    boolean sel1 = testPin(i, LOW);
    boolean sel2 = testPin(i, HIGH);
    if (sel1 && sel2) ADCSRA = 128 + i;
  }
}

boolean testPin(byte pin, boolean value) {
  pinMode(pin, OUTPUT);
  digitalWrite(pin, value);
  pinMode(pin + 1, INPUT_PULLUP);
  if (digitalRead(pin + 1) == value) return true;
  return false;
}

Далее приведен код обнаружения разности фаз между двумя сигналами. Подайте два низкочастотных сигнала на аналоговые контакты. Будет вычислена и отображена функция взаимной корреляции.



#include "Tx.h"

void setup() {
  Seriell_begin(115200);
  cr();
  pinMode(7, OUTPUT);
}

const int N = 500;
const int N2 = N / 2;
int a[N], b[N];

void loop() {
  setAdcPrescaler();
  asm("sbi 11,7");
  for (int i = 0; i < N; i++) {
    a[i] = analogRead(A0);
    b[i] = analogRead(A5);
  }
  asm("cbi 11,7");
  //delay(1); return;
  long ma = 0;
  long mb = 0;
  for (int i = 0; i < N; i++) {
    ma = ma + a[i];
    mb = mb + b[i];
  }
  ma = ma / N;
  mb = mb / N;
  int mc = -1;
  for (int i = 0; i < N; i++) {
    a[i] = a[i] - ma;
    mc = max(mc, a[i]);
    b[i] = b[i] - mb;
    mc = max(mc, b[i]);
  }
  const int tl = 800;
  const long amp = 200;
  const int n10 = N / 10; 
  byte i10 = 0;
  long maxNew = -1E8;
  for (int i = 0; i < N; i++) {
    tx_N(tl + amp * a[i] / mc); // blue
    tab();
    tx_N(tl + amp * b[i] / mc); //red
    tab();
    tx_N(tl);  // green
    tab();
    long cc = 0;
    for (int j = 0, k = i - N2; j < N; j++, k++)
      if (k >= 0 && k < N) cc = cc + (long) a[j] * b[k];
    tx_N(4 * cc / mc / mc);
    // -----------------------------------
    tab();
    if (cc > maxNew) {
      maxNew = cc;
      //phase = i - N2;
      tx_N(0);
    }
    else tx_N(200);
    tab();
    if (i == N2) tx_N(-250);
    else if (i + 1 > i10 * n10) {
      tx_N(-50);
      i10++;
    }
    else tx_N(0);
    cr();
  }
  delay(1000);
}

void setAdcPrescaler() {
  for (byte i = 2; i < 8; i++) {
    boolean sel1 = testPin(i, LOW);
    boolean sel2 = testPin(i, HIGH);
    if (sel1 && sel2) ADCSRA = 128 + i;
  }
}

boolean testPin(byte pin, boolean value) {
  pinMode(pin, OUTPUT);
  digitalWrite(pin, value);
  pinMode(pin + 1, INPUT_PULLUP);
  if (digitalRead(pin + 1) == value) return true;
  return false;
}



© digitrode.ru


Теги: Arduino




Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Комментарии:

Оставить комментарий