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);
}
}
Vamos agora alterar a rotina loop() para decodificar a identificação da emissora enviada através de grupos do tipo A0 ou B0 (código adaptado do artigo mencionado acima). Nestes grupos o bloco D possui dois caracteres e o bloco B informa a posição destes caracteres em um buffer de 8 posições.#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);
}
Executando este código com a emissora que estou usando para testes, deu para perceber dois problemas:- 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;
}
No próximo post vamos experimentar uma biblioteca mais sofisticada, que decodifica vários tipos de grupos. De qualquer forma, está claro que o suporte a RDS no RDA5807M está longe de ser perfeito.

Nenhum comentário:
Postar um comentário