Визуализатор спектра

Тема в разделе "Arduino & Shields", создана пользователем rbibokon, 23 май 2017.

  1. rbibokon

    rbibokon Нерд

    поменял значения очень быстро меняются и разобрался почему с 250000 не работало, и значения скачат от 0 до 500 помоему просто очень быстро менялись они и я не успевал следить.
     
  2. mcureenab

    mcureenab Гуру

    просто выдергиваете USB кабель и поток прекращается.
    Если от 0 до 500, то нужно поднять уровень напряжения тишины до 2.5 вольт.

    zero.png
    R1 сверху подключить на 5В. На выходе (к ADC Input) должно быть 2.5 В

    Конденсатор C берите от 5 мкФ. тогда срез НЧ будет до 20 Гц.

    http://sim.okawa-denshi.jp/en/CRtool.php

    тут R = 10k/2, поскольку ток течет через оба резистора.

    еще к этой сехеме нужно последовательно прицепить НЧ фильтр.

    [​IMG]

    R = 5000 Ом, C = 100нФ.

    http://sim.okawa-denshi.jp/en/CRtool.php

    от 100 Гц сигнал будет ослабевать.
     
    Последнее редактирование: 27 май 2017
  3. mcureenab

    mcureenab Гуру

    Собрал я эту схему

    [​IMG]

    Вместо R1 и R2 взял потенциометр на 10кОм. Крайние выводы подключил к GND и 5 В, средний к A0 и конденсатору. Не включая генерацию тона выставил уровень так, чтобы data[0] = 0. В общем R1 = R2 = 5 кОм.

    C = 10мкФ - электролитический, минус к источнику сигнала.
    Источник - программа tone generator с google смартфона. правый канал. синус.
    Сигнал слабоват.
    Но довольно острый пик получается.

    Код (C++):
    // Dump_A0
    #include "fix_fft.h"

    #define ASIZE 128

    void setup() {
      // put your setup code here, to run once:
      pinMode(A0, INPUT);
      Serial.begin(250000);
      while(!Serial){  }
      Serial.println(F("Starting..."));
    }

    void loop() {
      // put your main code here, to run repeatedly:
      char data[ASIZE + 1];
      char im[ASIZE + 1];
      unsigned long us1(0);
      for ( size_t i = 0 ; i < ASIZE + 1 ; ++i ) // Сэмпл 0 делаем для настройки фазы
      {
        unsigned long us2 = micros();
        data[i] = ( analogRead(A0) - 511 ) / 1;
    //    0;
        im[i] = ( us2 - us1 ) - 120;
        us1 = us2;
      }
      Serial.print("\t");
      Serial.println((int)data[0]);
      fix_fft(data + 1, im + 1, 7, 0);
      for ( size_t i = 1 ; i < ASIZE / 2 ; ++i )
      {
        Serial.print("\t");
    // Выводим значение амплитуды на частоте i.
        Serial.print(sqrt(data[i] * data[i] + im[i] * im[i]), 0);
      }
      Serial.println("");
      delay(500);
    }

     
    на реальной музыке еще НЧ фильтр нужен будет.
     
    Последнее редактирование: 27 май 2017
  4. mcureenab

    mcureenab Гуру

     
  5. mcureenab

    mcureenab Гуру

    Частота выше частоты сэмплирования повторно попадает на график.
    Как бы отражается от верхней грани и возвращается к нижней, затем отражается от нижней и т.д.
     
    Последнее редактирование: 27 май 2017
  6. mcureenab

    mcureenab Гуру

    Еще посоветую столбики суммировать не равными вырезками из массива data по 8 элементов , а с привязкой к октавам.

    data[1] и data [2] отличаются на октаву, data [2] и data [4] тоже отличаются на октаву, 4 - 8 и т.д. Т.е. размер вырезки для столбиков долден расти в геометрической прогрессии.

    Инфразвук, видимо, можно не выводить.
     
    Последнее редактирование: 27 май 2017
  7. rbibokon

    rbibokon Нерд

    Ой все это очень сложно.... че то я запутался))) видимо задача не для новичка)))
    Твой код что показывает? моно ли его преобразовать что бы показывал на экране?
     
  8. mcureenab

    mcureenab Гуру

    В мониторе порта будет гистограмму рисовать собачками.

    Код (C++):
    // Dump_A0

    #define prog_int8_t int8_t

    #include "fix_fft.h"

    #define ASIZE 128

    void setup() {
      // put your setup code here, to run once:
      pinMode(A0, INPUT);
      Serial.begin(250000);
      while(!Serial){  }
      Serial.println(F("Starting..."));
    }

    void loop() {
      // put your main code here, to run repeatedly:
      char data[ASIZE + 1];
      {
        char im[ASIZE + 1];
        unsigned long us1(0);
    // оцифровка фрагмента
        for ( size_t i = 0 ; i < ASIZE + 1 ; ++i )
        {
          unsigned long us2 = micros();
          data[i] = ( analogRead(A0) - 511 ) / 1;
          im[i] = 0;
    //    im[i] = ( us2 - us1 ) - 120; // Фаза сэмпла время от времени сдвигается. Но не ясно как её учитывать.
          us1 = us2;
        }
        Serial.println((int)data[0]); // для настройки 0.
    // Преобразование Фурье и расчет модулей амплитуды.
        fix_fft(data + 1, im + 1, 7, 0);
        for ( size_t i = 1 ; i < ASIZE / 2 ; ++i )
        {
          data[i] = sqrt(data[i] * data[i] + im[i] * im[i]) / 1; // Расчет амплитуды и масштабирование.
        }
    // Подготовка гистограммы.
        for ( size_t j = 0 ; j < 8 ; ++j ) // матрица 8 стоблцов
        {
          size_t k = j * 8;
          char max_(0);
          for ( size_t i = 0 ; i < 8 ; ++i )
          {
            max_ = max(max_, data[k+i+1]);
          }
          data[j] = (int)max_ * 8 / 45;
    //    data[j] = ( (int)data[k+1] + data[k+2] + data[k+3] + data[k+4] + data[k+5] + data[k+6] + data[k+7] + data[k+8] ) / 15; // Группировка по столбцам матрицы
        }
      }
    // Вывод гистограммы в Serial.
      for ( uint8_t j = 0 ; j < 8 ; ++j ) // заполняем матрицу
      {
        for ( size_t i = 0 ; i < 8 ; ++i )
        {
         if ( j < data[i] )
           Serial.print('@', 0);  // включаем ячейку
         else
           Serial.print(' ', 0); // выключаем ячейку
        }      
        Serial.println("");
      }
      delay(500); // Задержка чтобы монитор порта не перегружать
    }
     
     
  9. rbibokon

    rbibokon Нерд

    Какими еще собачками?)
    Я правильно понима что схема такая е как и у меня шнур с деком и проводами и через телефон звук? а выход сигнал тф смотришь в мониторинге порта?
     
  10. mcureenab

    mcureenab Гуру

    Собачки - @@@@
    Телефон, потом такая схема (вход с телефона на signal in, ADC Input на Arduino, R1 к 5 Вольт ),
    [​IMG]
    потом ардуинка и вывод в монитор порта.
     
  11. rbibokon

    rbibokon Нерд

    ADC Input на Arduino это куда? И R1 куда подклчается?
     
  12. mcureenab

    mcureenab Гуру

    ADC Input -> A0.
    R1 -> 5V
    R2 -> GND
    C = 10uF. Электролитический. Минус к signal in.
    R1 = R2 = 10kОм.
     
  13. mcureenab

    mcureenab Гуру



    Код (C++):
    // Dump_A0

    #define prog_int8_t int8_t

    #include "fix_fft.h"

    #define ASIZE 128

    const size_t buket_sz[8] = { 1, 2, 2, 4, 6, 10, 15, 23};

    void setup() {
      // put your setup code here, to run once:
      pinMode(A0, INPUT);
      Serial.begin(250000);
      while(!Serial){  }
      Serial.println(F("Starting..."));
    }

    void loop() {
      // put your main code here, to run repeatedly:
      char data[ASIZE + 1];
      {
        char im[ASIZE + 1];
        unsigned long us1(0);
        bool overload(false);
        for ( size_t i = 0 ; i < ASIZE + 1 ; ++i )
        {
          unsigned long us2 = micros();
          int v = analogRead(A0) - 511;
          if ( 400 < abs(v) ) overload = true;
          data[i] = v / 1;
          im[i] = 0;
    //    im[i] = ( us2 - us1 ) - 120; // Фаза сэмпла время от времени сдвигается. Но не ясно как её учитывать.
          us1 = us2;
        }
        if ( overload ) Serial.print("OVL\t");
        Serial.println((int)data[0]);
        fix_fft(data + 1, im + 1, 7, 0);
        for ( size_t i = 1 ; i < ASIZE / 2 ; ++i )
        {
          data[i] = sqrt(data[i] * data[i] + im[i] * im[i]) / 1; // Расчет амплитуды и масштабирование.
        }
      }
        size_t k = 1;
        for( uint8_t i = 0; i < 8 ; ++i )
        {
          unsigned long datum = 0;
          for ( uint8_t j = 0 ; j <  buket_sz[i]; ++j, ++k )
          {
    //        datum += data[k];
              datum = max(datum, data[k]);
          }
          data[i] = datum; //  / buket_sz[i];
          Serial.print('\t');
          Serial.print((int)data[i]);
        }
        Serial.println("\n@@@@@@@@");
        for ( uint8_t j = 0 ; j < 16 ; ++j ) // заполняем матрицу
        {
          for ( size_t i = 0 ; i < 8 ; ++i )
          {
           if ( j < data[i] / 5 )
             Serial.print('@', 0);  // включаем ячейку
           else
             Serial.print(' ', 0); // выключаем ячейку
          }      
          Serial.println("");
        }
        delay(500); // Задержка чтобы монитор порта не перегружать
    }
     
     
  14. rbibokon

    rbibokon Нерд

    Я так понял вот 8 столбиков собачки и есть частоты? так? А почему тогда сначала частоты растут до 3000Гц примерно(то есть в последнем столбце больше всего собачек). а после начинается с первого столбца уе до 20кГц ? и моно ли этих собачек перенести на мо матрицу диодную?
     
  15. mcureenab

    mcureenab Гуру

    Переноси на матрицу. У неё же каждый пиксель можно включать и выключать. В циклах (i, j) координаты пикселя i - столбик, j - строчка. Кроме того data сравнивается с уровнем строчки j, чтобы понять, достает столбик до этой строчки или нет.
    Заменить надо
    Serial.print('@', 0);// включаем ячейку
    и
    Serial.print(' ', 0);// выключаем ячейку

    и убрать весе упоминания Serial.

    Только размер поправь. у меня высота 16, потому как предусилителя нет и подогнать амплитуду под размер матрицы только делением data можно.

    Код (C++):
        for ( uint8_t j = 0 ; j < 16 ; ++j ) // заполняем матрицу
        {
          for ( size_t i = 0 ; i < 8 ; ++i )
     
    Возвратно поступательное движение пика амплитуды связано со спецификой алгоритма FFT. Частота выше частоты сэмплирования не выпадает, а отражается от грани. Если в спектре нет таких частот, то все нормально. Но в музыке частоты выше 4.5 кГц встречаются практически всегда. Поэтому на входе нужен фильтр НЧ. По моему в модуле http://amperka.ru/product/troyka-line-in-mini-jack фильтр есть. И установка 0 тоже есть: http://forum.amperka.ru/attachments/zero-png.8741/

    Еще вот это
    Код (C++):
    //        datum += data[k];
              datum = max(datum, data[k]);
     
    на монотонном сигнале эта формула

    datum += data[k]

    плохо работает на ВЧ. В области ВЧ один столбик включает 23 частотных диапазона. Поэтому если его поделить на 23, получится очень мало. В музыке скорее всего эта полоса будет использована более активно. В общем тут надо попробовать и max брать и среднее, а может и комбинировать.
     
    arkadyf нравится это.
  16. rbibokon

    rbibokon Нерд

    Все подключил как надо вроде заработало с собачками. Теперь это как то надо перенести на матрицу и еще. как мне убедить преподавателя что показывает именно частоты? то есть какой столбик отвечает за какую частоту?
    И еще я завтра смогу взять эту тройку шилд аудиовход. mcureenab моет в скайпе? Буду очень благодарен за помощь, просто в скайпе побыстрее было бы
     
    Последнее редактирование: 30 май 2017
  17. mcureenab

    mcureenab Гуру

    Почитайте про быстрое преобразование фурье.

    В иделае массивы data и im должны быть заполены значениями, которые получены с равными интервалами. Иными словами сэмплы должны поступать с постоянной частотой.

    Затем частота сэмплирования f делится на размер массива data 128 и с этим шагом вычисляются гармоники. Первый элемент массива содержит постоянное значение напряжения, второй элемент содержит амплитуду составляющей с частотой f/128, третий элемент с частотой 2f/128, следующий 3f/128 и т.д. до f/2. Т.е. пол массива. Оставшаяся половина массива повторяет первую половину. Её можно игнорировать.

    Чтобы понять какие частоты охватывает столбик нужно измерить частоту вызова analogRead.Сразу скажу, что эта частота не очень стабильна, что негативно сказывается на качестве результата.
    Тут это примерно 9.5кГц.

    Для демонстрации и защиты проекта следует использовать генератор синусоидального сигнала с выводом максимальной амплитуды.
    Так мы увидим, что максимум переползает от столбика к столбику в след за частотой.
    Модуль джека скорее всего содержит фильтр НЧ.
    Можно продедемонстрировать результат подключая источник с фильтром и без фильтра.

    Для музыки вероятно следует использовать другую формулу преобразования интервала частот в столбик. И молуль с фильтром НЧ.


    В МК есть режим непрерывного сэмплирования с равными интервалами. Но этот режим нужно программировать.
     
  18. rbibokon

    rbibokon Нерд

    Почему не хочешьо в скайпе?)
     
  19. rbibokon

    rbibokon Нерд

    Взял аудиовход. Что мне с ним делать?
     
  20. mcureenab

    mcureenab Гуру

    В к mini-jack подключить источник звука. G на GND, V на 5 V, L или R к аналоговому входу Arduino. Чтобы смешать оба канала (Left и Right) установить джампер.

    http://amperka.ru/product/troyka-line-in-mini-jack
     
    Последнее редактирование: 31 май 2017