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