O objetivo do RDS é permitir às estações de rádio transmitir pequenas quantidades de dados. Os usos típicos são o envio da identificação da estação, informações sobre o que está sendo transmitido (como nome e interprete da música) e informações sobre o transito.
Onde estes dados são colocados no sinal? Originalmente a transmissão FM enviava somente um canal de som, com frequências de 30Hz a 15KHz. O sistema foi expandido para envio de dois canais (som estéreo) colocando informação adicional em torno dos 38KHz. Um sinal contínuo de 19KHz indica a presença desta informação adicional. Os dados do RDS são enviados em torno dos 57KHz (3*19KHz). A figura abaixo (by Arthur Murray - http://en.wikipedia.org/wiki/File:RDS_vs_DirectBand_FM-spectrum2.png) mostra as várias informações que podem ser transmitidas por uma estação FM.
Os dados são transmitidos a uma taxa ligeiramente inferior a 1200bps (57000/48 bps para ser preciso). A transmissão é feita por grupos de 104 bits, divididos em quatro blocos de 26 bits. Estes 26 bits correspondem a 16 bits de informação mais 10 bits para detecção e correção de erros.
O primeiro bloco (bloco A) contém sempre o "código de identificação do programa" (PI code) que, nos EUA, informa o prefixo da emissora. O segundo bloco (bloco B) informa o tipo de grupo, que determina o que está codificado nos blocos C e D. Daí em diante a coisa fica ainda mais complicada, a norma americana pode ser vista aqui.
O RDA5807M possui toda a lógica para detectar, decodificar e conferir um grupo, disponibilizando os dados dos quatro blocos em quatro registradores de 16 bits. Uma limitação é que ele não efetua as correções, nem disponibiliza os bits necessários para a correção. Estranhamente, são indicados os erros encontrados apenas nos blocos A e B. Portanto não temos como saber se os dados nos blocos C e D estão corretos.
Uma outra dificuldade é saber quando um novo grupo está disponível. Existe um bit no registrador de status para isto, mas não encontrei uma forma de desligá-lo após ler os registradores. Este artigo sugere desligar e ligar a função de RDS, mas fiquei com a impressão que a recepção de um novo grupo demora bastante quando isto é feito. Outra forma, usada pela biblioteca que vou usar em testes futuros, é simplesmente ver se mudou em relação à leitura anterior.
Como primeiro teste, vamos experimentar detectar a recepção de grupos através da comparação e mostrar os blocos em hexadecimal. O código abaixo usa a mesma biblioteca do post anterior.
- #include <Wire.h>
- #include <RDA5807M.h>
- // Estação para teste, escolha uma estação
- // que você sabe que envia RDS
- const word ESTACAO = 9690; // 96,9MHz
- RDA5807M radio;
- word bloco[4] = { 0, 0, 0, 0 };
- byte regblc[4] =
- {
- RDA5807M_REG_RDSA, RDA5807M_REG_RDSB,
- RDA5807M_REG_RDSC, RDA5807M_REG_RDSD
- };
- void setup()
- {
- // Inicia a serial
- Serial.begin(9600);
- // Inicia o rádio
- radio.begin(RDA5807M_BAND_WEST);
- radio.mute();
- sintoniza (ESTACAO);
- radio.unMute(true);
- // Tempinho para começar a receber RDS
- delay (500);
- // Informa o status
- word frequency = radio.getFrequency();
- Serial.print(F("Sintonizado em "));
- Serial.print(frequency / 100);
- Serial.print(".");
- Serial.print(frequency % 100);
- Serial.println(F("MHz FM"));
- Serial.print(F("RSSI = "));
- Serial.print(radio.getRSSI());
- Serial.println("dBuV");
- word status = radio.getRegister(RDA5807M_REG_STATUS);
- if(status & RDA5807M_STATUS_RDSR)
- Serial.println(F("* RDS Group Ready"));
- if(status & RDA5807M_STATUS_STC)
- Serial.println(F("* Seek/Tune Complete"));
- if(status & RDA5807M_STATUS_RDSS)
- Serial.println(F("* RDS Decoder Synchronized"));
- if(status & RDA5807M_STATUS_ST)
- Serial.println(F("* Stereo Reception"));
- }
- void loop()
- {
- if (atlBlocos())
- {
- for (int i = 0; i < 4; i++)
- {
- printReg(bloco[i]);
- }
- Serial.println();
- }
- delay (10);
- }
- // Atualiza os blocos
- // Retorna true se houve mudança
- byte atlBlocos()
- {
- byte mudou = false;
- word status = radio.getRegister(RDA5807M_REG_STATUS);
- word erros = radio.getRegister(RDA5807M_REG_RSSI) &
- (RDA5807M_BLERA_MASK | RDA5807M_BLERB_MASK);
- if((status & RDA5807M_STATUS_RDSR) && (erros == 0))
- {
- for (int i = 0; i < 4; i++)
- {
- word aux = radio.getRegister(regblc[i]);
- if (aux != bloco[i])
- {
- bloco[i] = aux;
- mudou = true;
- }
- }
- }
- return mudou;
- }
- // Imprime um tegistrador em hexa
- const char hexa[] = "0123456789ABCDEF";
- void printReg (word val)
- {
- char aux[6];
- aux[0] = hexa[(val >> 12) & 0x0F];
- aux[1] = hexa[(val >> 8) & 0x0F];
- aux[2] = hexa[(val >> 4) & 0x0F];
- aux[3] = hexa[val & 0x0F];
- aux[4] = ' ';
- aux[5] = 0;
- Serial.print(aux);
- }
- // Sintoniza em uma frequência
- // (esta rotina está faltando na biblioteca)
- #define RDA5807M_TUNE_BIT word(0x0010)
- void sintoniza (word freq)
- {
- // verifica a programação de banda e espaçamento
- word band = radio.getRegister(RDA5807M_REG_TUNING) &
- (RDA5807M_BAND_MASK | RDA5807M_SPACE_MASK);
- const byte space = band & RDA5807M_SPACE_MASK;
- band = (band & RDA5807M_BAND_MASK) >> 2;
- // calcula o valor do canal
- freq -= pgm_read_word(&RDA5807M_BandLowerLimits[band]);
- word channel = (freq*10)/pgm_read_byte(&RDA5807M_ChannelSpacings[space]);
- // efetua a sintonia
- radio.updateRegister(RDA5807M_REG_TUNING, RDA5807M_TUNE_BIT, 0);
- radio.updateRegister(RDA5807M_REG_TUNING,
- RDA5807M_CHAN_MASK | RDA5807M_TUNE_BIT,
- (channel << RDA5807M_CHAN_SHIFT) | RDA5807M_TUNE_BIT);
- // aguarda sintonizar
- for (int i = 0; i < 10; i++)
- {
- word status = radio.getRegister(RDA5807M_REG_STATUS);
- if(status & RDA5807M_STATUS_STC)
- break;
- delay (100);
- }
- }
- #define RDS_GROUP 0xF800
- #define RDS_GROUP_A0 0x0000
- #define RDS_GROUP_B0 0x0800
- char nome[9] = "????????";
- void loop()
- {
- if (atlBlocos())
- {
- if (((bloco[1] & RDS_GROUP) == RDS_GROUP_A0) ||
- ((bloco[1] & RDS_GROUP) == RDS_GROUP_B0))
- {
- byte offset = (bloco[1] & 0x03) << 1;
- char c1 = (char)(bloco[3] >> 8);
- char c2 = (char)(bloco[3] & 0xFF);
- nome[offset] = c1;
- nome[offset+1] = c2;
- Serial.println (nome);
- }
- }
- delay (10);
- }
- Ocorrem com frequência erros no bloco D. Uma solução é só aceitar o bloco se for recebido igual mais de uma vez consecutiva.
- Apesar da norma recomendar não mudar o texto em intervalo inferior a 1 minuto, esta emissora (e outras que eu testei) muda o texto a cada poucos segundos para enviar o seu slogan.
- char nome_a[9] = "????????";
- char nome_b[9] = "????????";
- void loop()
- {
- if (atlBlocos())
- {
- if (((bloco[1] & RDS_GROUP) == RDS_GROUP_A0) ||
- ((bloco[1] & RDS_GROUP) == RDS_GROUP_B0))
- {
- if (atlNome())
- {
- Serial.println (nome);
- }
- }
- }
- }
- // Atualiza o nome
- // Retorna true se tem um novo nome
- byte atlNome()
- {
- // extrai a posição e os caracteres
- byte offset = (bloco[1] & 0x03) << 1;
- char c1 = (char)(bloco[3] >> 8);
- char c2 = (char)(bloco[3] & 0xFF);
- byte igual;
- // despreza caracteres absurdos
- if ((c1 < 0x20) || (c1 > 0x7E))
- return false;
- if ((c2 < 0x20) || (c2 > 0x7E))
- return false;
- // só atualizar nome_a depois de receber
- // duas vezes igual
- if (nome_b[offset] == c1)
- {
- nome_a[offset] = c1;
- nome_b[offset] = '?';
- }
- else
- {
- nome_b[offset] = c1;
- }
- if (nome_b[offset+1] == c2)
- {
- nome_a[offset+1] = c2;
- nome_b[offset+1] = '?';
- }
- else
- {
- nome_b[offset+1] = c2;
- }
- // verifica se recebeu um nome completo
- for (int i = 0; i < 8; i++)
- {
- if (nome_a[i] == '?')
- return false;
- }
- // atualizar o nome e preparar para receber o
- // próximo
- igual = true;
- for (int i = 0; i < 8; i++)
- {
- if (nome[i] != nome_a[i])
- igual = false;
- nome[i] = nome_a[i];
- nome_a[i] = nome_b[i] = '?';
- }
- return !igual;
- }
Nenhum comentário:
Postar um comentário