Ajuda com sine wave

Software e Hardware para uC PIC

Moderadores: andre_luis, 51, guest2003, Renie

Ajuda com sine wave

Mensagempor Vonnilmam » 17 Jun 2009 19:14

Caros colegas,

Estou iniciando um trabalho novo e necessito obter informações de como criar uma senoide com resolução de 16 bits, cuja sua frequencia possa ser controlada num range de 20 hz até 20khz (linearmente de 1 em 1 hz)!

Estou utilizando um DSPIC 30F4013 e um dac de audio multicanal com resolução maxima de 24 bits da cirrus cs4333 e estou programando em C (achava C muito complicado, mas agora que provei da coisa, acho muito mais facil, dou o braço a torcer, apesar de gostar muito do assembler).

No meu hardware eu tenho além do dac uma flash de 32 megbit x 16 bits da atmel 49bv322dt, estou pensando em armazenar os dados nessa flash!

Porém minha duvida são todas, mas principalmente qual seria o melhor metodo: Criar uma tabela (contendo a curva da senoide) ou fazer a senoide via software utilizando os recursos matemaricos do DSPic. A maior questão esta em como gerar a senoide e como controlar a frequencia da mesma externamente!...


Muito obrigado
VonNilmam "Assembler" e agora "C"
Avatar do usuário
Vonnilmam
Byte
 
Mensagens: 446
Registrado em: 19 Out 2006 14:25
Localização: espacial

Mensagempor EDSONCAN » 17 Jun 2009 19:42

Os dois metodos devem dar certo, mas acredito que por tabela seja mais facil.
Edson
EDSONCAN
Word
 
Mensagens: 876
Registrado em: 11 Out 2006 14:11

Mensagempor KrafT » 17 Jun 2009 19:51

Se vc usa tabela, com numero suficiente de entradas, a frequencia vai estar diretamente proporcional a velocidade que vc le a tabela e joga no DAC.

Se vc usar funcao seno, muito provavelmente o compilador vai usar tabelas tambem, eventualmente otimizadas, mas creio que vai usar.
Avatar do usuário
KrafT
Dword
 
Mensagens: 2228
Registrado em: 11 Out 2006 14:15
Localização: Blumenau -SC

Mensagempor msamsoniuk » 17 Jun 2009 23:32

o problema eh que para ter uma saida perfeita de 1Hz a 20KHz, vc precisaria ter um sample rate de 40KHz e uma senoide de referencia com 40 mil amostras armazenada, ou seja, 80KB, supondo que vc vai usar um dac de 16 bits.

uma solucao seria gerar o seno na hora, mas vai desperdicar uma boa capacidade de processamento fazendo isso. uma solucao mais economica e precisa seria usar um filtrinho:

y[n] = k1*y[n-1] + k2*y[n-2]

neste caso vc precisa apenas calcular k1, k2, y[n-1] e y[n-2] iniciais e entao o seu algoritmo se reduz a usufluir da performance da instrucao mac (multiplica e acumula) do seu dsp :)

o valor de k2 eh sempre -1, o valor inicial de y[n-1] eh 0. os outros complicam um pouco mais:

k1 = 2*A*cos(t)
y[n-2] = A*sin(t)

sendo A a amplitude (32768 por exemplo) e t dependente da frequencia do tom (f) e o sample rate (fs):

t = 2*PI*f/fs

tendo os coeficientes, basta setar um timer periodico para o sample rate que vc usou nas contas e ele vai gerar qq tom. como os coeficiente sao calculados apenas na inicializacao do gerador de tom, vc pode desperdicar um pouco de processamento e usar ponto flutuante para melhorar a precisao.

o codigo ficaria algo como:

Código: Selecionar todos
unsigned int y1,y2,y3;
unsigned k1;

start_tone(unsigned f, unsigned fs)
{
  float k2;
  k2 = 2*PI*f/fs;
  k1 = cos(k2)*65536;
  y1 = sin(k2)*32768;
  y2 = 0;
  set_timer(fs); /* configura timer para frequencia fs */
}

timer_interrupt()
{
  y3 = (k1*y2)/32768-y1;
  y1 = y2;
  y2 = y3;
  set_dac(y3); /* joga valor no dac */
}

int main()
{
  start_tone(800,8000); /* inicia gerador de tom com 800Hz e sample rate de 8KHz */
  while(1);
}


bom, a teoria eh mais ou menos essa... eu nao testei este codigo (eh apenas ilustrativo!), mas testei isso em uma planilha de calculo e eh um algoritmo gerador de tons muito comum por ae (tem na wikipedia, procure por goertzel). mesmo forcando os coeficientes para serem inteiros de 16 bits e um sample rate arbitrario, ele gera tons muito bem, inclusive com frequencia quebrada! :)

uma limitacao desse algoritmo eh que ele pode gerar tons com frequencia maxima de 1/4 do sample rate. entao para gerar 20KHz, vc precisa ajustar o sample rate para 80KHz. mas como este metodo eh muito veloz, vc pode aumentar o sample rate lah em cima e gerar uma faixa de tons muito maior e com muito mais precisao.
Editado pela última vez por msamsoniuk em 18 Jun 2009 00:57, em um total de 1 vez.
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor barboza » 18 Jun 2009 00:52

Considerando um DAC de 16 bits, acho que maior tabela seria de 2^16 = 65K e o resto, mesmo que com sample rate maior é redundância.

Você realmente precisa dessa precisão no DAC?

Neste caso como como é senoide, periódica, pode controlar a fase e dividir esta tabela por 2 ou ate mesmo por 4.

A opção que o Marcelo citou é interessante. Apesar de continuar usando as funções sin e cos, pois só as usam no inicio da geração do sinal e a execução da interrupção é curta, talvez até menos que o uso de tabela.


Marcelo!

Acho que as variáveis x1 e x2 seriam na realidade y1 e y2, respectivamente.
Os homens mentiriam muito menos se as mulheres fizessem menos perguntas.
Avatar do usuário
barboza
Word
 
Mensagens: 948
Registrado em: 17 Out 2006 13:42
Localização: Longe de onde gostaria de estar

Mensagempor msamsoniuk » 18 Jun 2009 03:49

pois eh, eu vi esse detalhe, corrigi ali e no fim das contas troquei tudo para x novamente para fazer testes... eh mania misturar x e y! :) hehehe

bom, eu estava fazendo alguns testes e percebi que este algoritmo tem mais algumas falhas e tem que tomar cuidado! a principal eh que os arredondamentos em funcao da precisao fazem a frequencia se desviar bastante. isso eh pq conforme vc arredonda, o erro se propaga para o proximo valor e assim por diante... qto maior a taxa de amostragem, mais erros se acumulam e a solucao eh melhorar a quantidade de bits.

para conseguir 1Hz a 80KHz de taxa de amostragem e erro de -4%, eu aumentei a precisao para 30 bits e usei registros de 32 e 64 bits no PC:

Código: Selecionar todos
#include <math.h>
#include <stdio.h>

long            k1;
long            x1, x2;
long long       x3;

start_tone(unsigned f, unsigned fs)
{
        double           k2;

        k2 = (2 * M_PI * f) / fs;
        k1 = cos(k2)*2*(1<<30);
        x1 = sin(k2)*(1<<30);
        x1 = -x1;
        x2 = 0;
}

timer_interrupt()
{
        x3 = k1;
        x3 *= x2;
        x3 >>= 30;
        x3 -= x1;

        printf("%d\n", x2);

        x1 = x2;
        x2 = x3;
}

int
main()
{
        start_tone(1, 80000);

        while (1)
                timer_interrupt();
}


para medir a saida e calcular o erro, redirecionei a saida considerando cada linha um sample e com outro script calculando a inversao de sinal da amostra. e claro, conforme a frequencia do sinal gerado aumenta, o erro desaparece. com ponto flutuante de 64 bits, o negocio fica tranquilo e eh possivel gerar na boa um sinal de 1Hz a 1MHz sem erros, mas usando uma capacidade de processamento tao cavalar que um PC bom teria dificuldades em realizar em tempo real...em essencia, esse algoritmo parece que nao eh para ser utilizado exatamente desta forma, ele se dah melhor quando as frequencias a serem geradas nao sao tao distantes da taxa de amostragem, o que significa que uma boa jogada seria o sistema ter taxa de amostragem dinamica ou entao partir para tabela de seno mesmo.

outros erros naquele exemplo inicial eh que o valor inicial de x1 estava com o sinal invertido e a multiplicacao de k1*x2 estava dando overflow! :)
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor proex » 18 Jun 2009 07:05

Se bem conheço o Vonnilmam, só faltou ele dizer que precisa de polifonia de 64 notas :lol: :lol: :lol:

Minha miseravél experiencia em áudio digital me ensinou que, em se tratando de sintese de sinais de áudio, a melhor coisa mesmo é usar Wave Tables.

Considerando que memorias são muito baratas atualmente, o uso de Wave Table permite gerar qualquer tipo de forma de onda sendo todas elas com o mesmo Sampling Rate.

Ficaria muito dificil sintetizar ondas mais complexas a partir de filtros oscilantes.

Ja brinquei muito com filtros passa banda digitais, estreitando o fator Q e controlando a realimentação até eles oscilarem.

Chega um momento em que fica dificil controlar os paramentros quando se altera a frequencia. A amplitude também muda e tudo isso tem que ser tratado no mesmo algoritimo de cada filtro, para cada frequencia.

Outra coisa interessante com filtros oscilantes é que, como teoricamente possue uma entrada, o que injetar nela para iniciar a oscilação?

Se ao iniciar o filtro com valor Zero na entrada, ele nao vai oscilar.

No meu caso, criei um oscilador de ruido rosa pra injetar nas entradas de todos os filtros.

A coisa foi ficando complexa demais pro meu gosto.

Bem, eu usaria Wave Tables.

.
Editado pela última vez por proex em 18 Jun 2009 07:13, em um total de 1 vez.
proex
Dword
 
Mensagens: 2101
Registrado em: 11 Out 2006 14:05
Localização: São Paulo

Mensagempor guest2003 » 18 Jun 2009 07:11

Ola,

Utilize um chip DDS , que foi desenvolvido exatamente para isso que vc precisa... a Analog Devices tem varios...

Ai voce pode controlar digitalmente de 20 a 20Khz em steps de 0.01Hz se quiser ;)

Se não encontra-los da um grito que vejo alguns part numbers pra vc... utilizei um em um projeto que precisava gerar uma senoide de 100Hz com extrema precisão...

[]'s
http://www.sethi.com.br (Institucional)
http://www.sethi3d.com.br (Impressoras 3d)
http://www.sethi.com.br/blog (Blog Impressoras 3d)
Avatar do usuário
guest2003
Word
 
Mensagens: 746
Registrado em: 13 Out 2006 11:48
Localização: Campinas - SP

Mensagempor Sergio38br » 18 Jun 2009 08:32

Bom dia, falando na analog device, tem este que vou utilizar em um projeto por aqui.

http://www.analog.com/static/imported-f ... AD9833.pdf

e ainda tem na farnell por uns R$39

[ ]'s
Sergio
Avatar do usuário
Sergio38br
Word
 
Mensagens: 759
Registrado em: 22 Nov 2007 13:39
Localização: São Paulo - SP

Mensagempor Vonnilmam » 19 Jun 2009 00:03

Muuuito obrigado a todos, em especial ao Marcelo Samsoniuk
e ao meu amigo PROEX... :lol: :lol:

Você acertou, preciso gerar na realidade pelo menos 72 canais de polifonia e o que é mais interessante controlar tudo via midi...heheh

Na verdade estou desenvolvendo um novo gerador musical, tomei como um desafio fazer isso (há sim, viva ao C...cara como esse treco é legal, exije pouco esforço neural "eu sou viciado em assembler" por isso falo assim).

Há proex, eu fiz o gerador de ritmos via wave table! Cara ficou perfeito o trem! Agora eu só preciso entender como misturar (somar) mais de um instrumento tocando ao mesmo tempo (estou usando sample rate à 22khz x 8 bits) até o momento estou enviando tudo para um DAC RC tradicional, mas a meta é enviar tudo para um dac digital tipo da cirrus cs4333 ou similar.

Eu já coloquei o dspc 30f4013 na bancada plugado num icd2, o software estou fazendo no mikro C...

Muito obrigado a todos, ha sim, eu não preciso necessariamente de 16bts de resolução na senoide, se eu conseguir gerar essa senoide com 8 bits já esta muito bom para não dizer ótimo, o meu problema esta em como desenvolver o algoritmo para controlar a frequencia exata, tipo gerar um frequencia que origine uma divisão "quebrada"...
VonNilmam "Assembler" e agora "C"
Avatar do usuário
Vonnilmam
Byte
 
Mensagens: 446
Registrado em: 19 Out 2006 14:25
Localização: espacial

Mensagempor msamsoniuk » 19 Jun 2009 02:40

se a resolucao eh limitada em 8 bits, uma tabela de 256 entradas jah resolveria o problema. criar a tabela eh relativamente facil, o problema eh como usar ela direito...

o primeiro truque eh calcular o comprimento de uma onda inteira:

width = sample_rate/frequency

se vc tem 22Khz de taxa de amostragem e quer, por exemplo, 1Hz, o comprimento de onda sera de 22000 unidades.

o segundo truque eh varrer a tabela proporcionalmente ao numero de unidades, seria algo como:

for(raster = 0; raster != width ; raster++)
{
value = table[raster * 256 / width];
}

nao sei se vc consegue enchergar ali, mas ele varre as 22000 unidades e faz uma regra de 3 simples para achar a posicao na tabela. com isso vc varre a tabela com qq frequencia que quiser.

ateh ae vc faz um unico tom (eh soh repetir o for que vc tem ele sendo gerado continuamente)... como fazer varios ? cria arrays para tudo isso. obviamente nao tem como fazer o for, entao vc troca por uma especie de loop paralelo onde cada contagem eh avaliada de forma independente.

o codigo que eu testei no PC ficou desta forma:

Código: Selecionar todos
#include <math.h>
#include <stdio.h>

char table[256];

make_table()
{
  int i;

  for(i=0;i!=256;i++)
  {
    table[i]=(char)(127.0*sin((i*M_PI*2.0)/255.0));
  }
}

int main()
{
  int i, raster[4], width[4], value[4],
      frequency[4]={ 1, 2, 4, 8 },
      sample_rate = 200;

  make_table();

  for(i=0;i!=4;i++)
  {
    width[i] = sample_rate/frequency[i];
    value[0] = raster[i] = 0;
  }

  while(1)
  {
    for(i=0;i!=4;i++)
    {
      value[i] = table[raster[i]*256/width[i]];
      raster[i]++;
      if(raster[i]==width[i])
      {
        raster[i]=0;
      }
    }

    printf("%d\n",(value[0]+value[1]+value[2]+value[3])/4);
  }
}


note que no final eu faco a mixagem dos canais. como sao 4 canais, eu somo todos e divido por 4. uma coisa legal que vc pode fazer eh adicionar pesos diferentes para cada canal (valores de 0 a 1) e entao dividir pela soma dos pesos :)

bom, no codigo de exemplo eu gerei frequencias de 1, 2, 4 e 8Hz com taxa de amostragem de 200Hz (o grafico disso ficou bonito). no caso de um DSP, teria que colocar aquele codigo do while(1) em uma interrupcao de timer para controlar a taxa de amostragem e dar uma revisada no tamanho das variaveis (no PC eh tudo 32 bits).

uma outra coisa importante: tente fazer range[i] = 0 quando ativar o tom e espere sempre range [i] ser igual a width [i] para desativar o tom (ou mudar a frequencia). isso garante que o valor na ativacao, mudanca ou desativacao seja 0, do contrario, se vc ativar, mudar ou desativar em qq lugar, vc vai ouvir os estalos das ondas quebradas pela metade! :)
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Mensagempor fabim » 19 Jun 2009 08:47

tchelo..

ME adiscurpa pela intromissão,,,

Mais me explica que P%$#A é essa...

Código: Selecionar todos
   width = sample_rate/frequency

se vc tem 22Khz de taxa de amostragem e quer, por exemplo, 1Hz, o comprimento de onda sera de 22000 unidades.

o segundo truque eh varrer a tabela proporcionalmente ao numero de unidades, seria algo como:

for(raster = 0; raster != width ; raster++)
{
value = table[raster * 256 / width];
}


Se me fiz entender, é pra saber quantas amostragens maximas para dentro de um ciclo inteiro de uma determinada frequencia ?

tipo 22KSPS em 16 bits.

22K / (F=T1+T2).
22k / 500 = 44 amostras por ciclo de 500hz..

Só por curiosidade mesmo, é que tu explicou sem explicar...rsrs

abraços

fabim
Mano, ve só.
Sou responsável pelo que escrevo!!! E não pelo que você entende !!!
fabim
Dword
 
Mensagens: 5001
Registrado em: 16 Out 2006 10:18
Localização: aqui uái!!!?

Mensagempor proex » 19 Jun 2009 12:48

Vonnilmam, pra somar as amostras, faça um OR entre elas e jogue o resultado no D/A.
proex
Dword
 
Mensagens: 2101
Registrado em: 11 Out 2006 14:05
Localização: São Paulo

Mensagempor Djalma Toledo Rodrigues » 19 Jun 2009 14:05

barboza escreveu:...Neste caso como como é senoide, periódica, pode controlar a fase e dividir esta tabela por 2 ou ate mesmo por 4... .

Isso. Dividindo a Tabela por 4:
Isso é possível pois uma Tabela da Senóide de 0 a 90° se repete nos outros quadrantes.
Então de 90 a 180° decrementa o Ponteiro da Tabela

Ao chegar a Zero um Flag sinaliza Semi ciclo negativo e o Ponteiro percorre a tabela normalmente como fez de 0 a 90°

De 180 a 360°, quarto quadrante, decrementa o Ponteiro, ao chegar a
Zero reseta o Flag.

E Loop rsrs

Mas isso para Senóide.

Estão falando agora em música

Ah! A Música ...
.
Avatar do usuário
Djalma Toledo Rodrigues
Dword
 
Mensagens: 2334
Registrado em: 03 Ago 2008 13:22

Mensagempor msamsoniuk » 20 Jun 2009 01:47

opa fabim! ficou meio obscuro sim, mas acho que eh isso que vc entendeu que eh! hehehe :)

a essencia desse algoritmo que eu postei eh justamente gerar um ciclo completo na frequencia que vc quer, com a taxa de amostragem fixa do sistema. no caso que vc citou, para gerar 500Hz com taxa de amostragem de 22KHz, seriam necessarias 44 amostras. com isso eu sei que aquele buffer de 256 amostras do seno tem que virar 44, entao eu faco um for() que justamente faz um "zoom" do buffer e transforma as 256 amostras em 44... pode parecer um pouco bizarro, mas esse algoritmo eu testei com audio depois de verificar que funcionava muito bem com zoom de imagens! hahaha

uma vantagem eh que vc pode usar com qq buffer de audio, nao precisa ser necessariamente a tabela do seno! ;)

outra aplicacao interessante dele eh para adaptar streams que operam em cadencias diferentes. por exemplo, um stream de audio de 22KHz que precisa ir para 8KHz consiste simplesmente em um buffer de entrada de 11 amostras na entrada e 4 amostras de saida:

Código: Selecionar todos
rate(int *in, int *out)
{
  for(i=0;i!=4;i++)
  {
    out[i] = in[i*11/4];
  }
}


alguma coisa assim... bom, uma falha do algoritmo eh bem obvia: vc em 11 amostras e escolhe 4 delas... sera q elas sao representativas ? pior ainda se fosse passar de 8KHz para 22KHz, pq surgiriam serrilhados no audio, justamente por falta de informacao na conversao! daih a solucao seria implementar anti-aliasing, ou seja, construir o buffer de amostras de saida baseando-se na interpolacao linear das amostras de entrada.

fabim escreveu:tchelo..

ME adiscurpa pela intromissão,,,

Mais me explica que P%$#A é essa...

Código: Selecionar todos
   width = sample_rate/frequency

se vc tem 22Khz de taxa de amostragem e quer, por exemplo, 1Hz, o comprimento de onda sera de 22000 unidades.

o segundo truque eh varrer a tabela proporcionalmente ao numero de unidades, seria algo como:

for(raster = 0; raster != width ; raster++)
{
value = table[raster * 256 / width];
}


Se me fiz entender, é pra saber quantas amostragens maximas para dentro de um ciclo inteiro de uma determinada frequencia ?

tipo 22KSPS em 16 bits.

22K / (F=T1+T2).
22k / 500 = 44 amostras por ciclo de 500hz..

Só por curiosidade mesmo, é que tu explicou sem explicar...rsrs

abraços

fabim
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Próximo

Voltar para PIC

Quem está online

Usuários navegando neste fórum: Google [Bot] e 1 visitante

cron

x