RDS 工作笔记

话说西元一千九百七十四年,欧洲有一帮不安分份子开始密谋策划,妄想利用有限的无线电频谱资源,去传送更多的资信讯息。六年后的1980年,他们在瑞士的伯尔尼进行了第一次的实用性试验。之后82年和83年又分别进行了各一次试验。至84年,首份正式的RDS技术规范EBU 3244横空出世----历经10年,RDS胚胎总算孕育成形;同年,美国福特公司开始汽车RDS收音机的研发工作。

85年,大规模的预运作试验在德国进行;同年,EUB(European Broadcast Union,欧洲广播联盟)确定了第一部实用RDS接收机面世的时间----1987年。

米国佬眼看欧洲在按部就班的进行着RDS的推广活动,再也坐不住了。1986年,他们在达拉斯向全国广播工作者协会推广介绍RDS系统。同年,国际无线电咨询委员会的RDS推荐文件正式发行。

好了,终于等到87年。在柏林IFA大展览会上,人们终于见到RDS接收机的倩影。德国VOLVO上市全球第一部RDS汽车收音机。88年,根德和菲力浦亦开始量产RDS汽车收音机。

再说回米国,直至89年,还在华盛顿D.C和拉斯维加斯向全国广播工作者协会介绍RDS系统----咳,由此可见,一项新鲜事物的接受程度哪怕是在科技发达的米帝国亦是步步唯艰。(哈哈,89年,诸位都干了些啥呢?)来到92年,米国的RDS国家标准----RBDS(Radio Broadcast Data System)技术规范才正式出炉。同年,RDS欧洲标准CENELEC EN 50067:1992出版发行。(注:CENELEC=欧洲电工技术标准化委员会)

RDS硬件规范
1)副载波导频:57KHz +/- 6Hz,对主载波标称频偏+/-1.0KHz 至 +/-7.5KHz;
2)调制方法:PSK(相移键控)
3)数据传输比特率:1187.5bit/s

进一步调查,知道的都说RDS家教甚好!举止文雅、谈吐不俗自不需多说,另外18般武艺也样样精通。都有些啥子本事?嘿嘿......
1。PI----节目识别。能使接收机依据节目身份代码正确切换。
2。PS----节目业务名称。使接收机能显示正在收听的电台节目业务名。
3。PTY----节目类型。随节目同时发出,共分31类,如音乐、体育、新闻等。
4。TP----交通节目识别。是一个开关信号,指明正在收听的节目业务是否播送交通公告的节目。
5。TA----交通公告识别。是一个切换信号,指明是否正在播送交通公告。
6。AF----替换频率表。此表给出在同一或相邻区域内播送同一节目的各发射机的频率信息。
7。DI----解码器识别。是一个切换信号,指明广播节目使用的16种工作模式的哪一种(或其合成)。
8。RT----广播文本。播音同时可在接收机显示屏显示的文本信息。
9。EON----增强的其他网络信息。有此功能的接收机既可收听调谐节目,又可通过EON功能收听相互参照的其他节目的交通公告。
10。CT----时间和日期。广播节目和日期码,使用UTC制式,并又接收机自动转为当地时。
11。EWS----紧急报警系统。紧急情况下使用的,接收机可自动调谐且识别此信息。
12。LN----定位和导航。给出发射台的位置信息,为导航和定位提供数据。
13。M/S----音乐/语言切换。听众可根据此信号调整接收机的音量和音调以达到最佳效果。
14。TMC----交通信息通道。用于传送交通编码信息,格式待定。
15。RP----广播寻呼。功能类似寻呼机。
16。IH----内部应用。由各个广播机构自行决定。
17。PIN----节目栏目号。广播电台所公布的预定的节目开始日期和时间,接收机用此设置预收听节目。
18。TDC----透明数据信道。用以传送图形数据的信道,有32个子信道。



检查揭示:RDS共有16组编码),每条上有四个块,每块里又各有26个bit。其中16bit是信息字,10bit是校验字。
首先,要将RDS信号运送出来

(RDS编码发送过程)

再看接应一方的手段

(RDS接收解码过程)

说到这里,该是进入实用话题的时候了。

话题一:关于RDS广播特点及RDS广播接收机

RDS系统除了传送实用的信息数据外,同时也传送开关信号。人们利用这套编码系统,从而实现FM频段的一台多频点的发射方案----方法是:一个电台,利用小功率的多个频点完成对某一区域的覆盖,从而替代单频点大功率的发射模式;优点是:节省发射功率,减少电磁污染,可以使覆盖范围信号强度均匀,避免强的过载,弱的收不到的烦恼。(手机移动通讯系统应该从这里偷了师)

RDS系统一开始开发,就有一个很特定目标:满足汽车收音的移动特性,使之能有满意的移动接收效果。所以,PI、TP、TA、AF、EON等编码都是为满足这个目的而设计的。至于家用收音机的RDS功能,只不过是拾人家一点牙惠而已。最近亦有便携式收音机做些汽车机的功能,呵呵,它也有移动特性啊。

问题:移动接收的效果是如何满足的?首先,每个RDS电台都有自己的身份证---PI码,然后AF码里面又包含有本台的各发射频率列表,利用这两个编码,接收机便能比较出哪一个频率的信号最强,使之始终接收最强的那个频点,并实现无缝切换。

另外,TA是一个强制执行代码。只要你在汽车里开着音响,哪怕是在听CD或播放磁带,只要有交通消息播报,接收机自动切回收音状态收听。播完后又自动回到原先的工作状态,想干啥继续干啥。
总之,RDS系统在交通管制上可以实现:
一、交通信息广播。
二、交通诱导(交通流量控制)。
三、紧急通告(强迫接收)。
四、单呼(BP机功能)。
五、群呼。
六、报失车辆的锁定与报警。



特别提示:不要指望你能买到一部收音机,它能具有RDS的所有功能。原因之一是RDS本身还在完善之中,之二就是制造商总是针对自己的目标市场选择性的做一些RDS功能。

花絮:曾经参与开发一款RDS汽车机,历时近两年(是干活的时间而不是抄作时间)。光是路试就在欧洲做了一次,香港做了三次,深圳做了两次。。。。。亦苦亦乐


(RDS汽车机)




(NEWS)




(CLASSIC MUSIC)




(SPORTS)






话题二:RDS在中国
1991年RDS首次在中国推介,至1995年,中华人民共和国国家标准《广播数据系统技术规范》出台(GB/T 15770-1995),也就是说RDS在中国的准生证有了。下面呢,我们化整为零说说RDS在中国的情况。

1。大陆

因Car—radio手头资料有限,真的部知道大陆甚麽地方在试播RDS信号,还望各位兄弟告知。
猜想没能迅速推广的原因:1)市场因数。需投入大量人力物力进行设备更新,需进行大量市场宣传。另外,设备不能自己生产亦是重要因数之一。2)技术因数。RDS技术引入中国时,DAB(数码音频广播)技术已经成熟。本着宁超前不滞后的原则,于是先行选择了DAB试播。


顺便贴一贴广东佛山电台数码广播在珠三角地区信号强度实测数据对应图



回澜33dB    罗源24dB  江谷30dB  广利32dB   
炭步35dB  钟落潭33dB  广州45dB  佛山85dB
黄埔39dB    石碣31dB  番愚34dB  厚街33dB
鹤山52dB    长安28dB  新垦28dB  宝安27dB
中山46dB    三江31dB  斗门32dB



下面是中国各省区RDS的识别码对应图,各位可对一下自己所在地是甚麽编码




2。香港
因收大英帝国长期殖民统治,香港的工业体制甚至生活习惯都深受英国影响。目前香港共16个广播频道。其中7个FM频道,除两个没有RDS功能外,剩下的都有。


有RDS功能之五个FM电台之频率和发射基站对应表(频率:MHz)

      电台名      商业电台1  商业电台2  香港电台4  劲歌    金曲
                    CR1        CR2        RTHK4    HIT    FM-S
发射基站

太平山              88.1      90.6      97.6      99.7    104.6 
飞鹅山              89.5      92.1      98.9      101.8    106.3     
青山                88.6      91.2      98.7      100.4    102.5
金山                88.9      90.9      98.4      101.6    105.5
笔架山              89.2      91.1      98.1      100.5    102.4
南丫岛              89.1      91.6      98.2      102.1    104.5
大雾山              88.3      90.7      97.8      100.0    104.7


以上列表还要港人及在香港的朋友收听验证。(深圳不能收到全部频点)


3。澳门

澳门虽曾是葡萄牙的殖民地,但因地域太小,估计未必采用RDS制式。这一点请珠海、中山、澳门和香港的朋友验证。


4。台湾

台湾一直跟在米国和***屁股后面,整天嗅些尾气。但米国的RBDS推广并不普及(***那边也不见啥动静),猜想台湾这方面还在按兵不动。这里烦请米乐.刘以及其他方便出入台湾的朋友来补充。

 

rds源代码解码:

View Code
   1 /*

   2 

   3 Copyright (C) 2006 Marc Ketel

   4 

   5 This program is free software; you can redistribute it and/or

   6 modify it under the terms of the GNU General Public License

   7 as published by the Free Software Foundation; either version 2

   8 of the License, or (at your option) any later version.

   9 

  10 This program is distributed in the hope that it will be useful,

  11 but WITHOUT ANY WARRANTY; without even the implied warranty of

  12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

  13 GNU General Public License for more details.

  14 

  15 You should have received a copy of the GNU General Public License

  16 along with this program; if not, write to the Free Software

  17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

  18 

  19 */

  20 

  21 #define INFO "20060629-1 RDS Decoder by Marc Ketel alias DiNo, [email protected]."

  22 /*

  23 

  24 Works with RDS clock signal and RDS data signal. During design a TDA7300B rds demodulator was used.

  25 

  26 This source compiles correctly with WinAVR 20060421.

  27 

  28 Use a Atmega168 to flash program into. low fuse: 0xF0, high fuse: 0xDD

  29 

  30 Versions:

  31   20060629-1  Initial version

  32 

  33 */

  34 

  35 /*

  36 

  37 general word on ISR: it does not seem that ISR has global interrupts disabled.

  38 

  39 */

  40 

  41 #include <avr/io.h>

  42 #include <avr/interrupt.h>

  43 #include <avr/pgmspace.h>

  44 #include <avr/wdt.h>

  45 #include <stdio.h>

  46 #include <math.h>

  47 

  48 #define RDS_PORT PIND

  49 #define RDS_PIN  PD4

  50 

  51 FILE* Uart;

  52 

  53 //used for stdout

  54 int uartSend(char data, FILE* stream) {

  55   //wait for completion of previous send

  56   loop_until_bit_is_set(UCSR0A, UDRE0);

  57 

  58   //send the data

  59   UDR0 = data;

  60   return 0;

  61 }

  62 

  63 //used for stdin

  64 int uartRecv(FILE* stream) {

  65   loop_until_bit_is_set(UCSR0A, RXC0);

  66   return UDR0;

  67 }

  68 

  69 volatile unsigned char event = 0;

  70 #define eventTimerOverflow      _BV(0)

  71 #define eventUsart0RxDInterrupt _BV(1)

  72 #define eventGroupComplete      _BV(2)

  73 #define eventBitstreamSnapshot  _BV(3)

  74 #define eventSyncLost           _BV(4)

  75 

  76 volatile unsigned char timerOverflowCount = 0;

  77 volatile unsigned char usart0RxDByte = 0;

  78 volatile unsigned char bitCounter = 0;

  79 volatile unsigned char bitCounterSnapshot = 0;

  80 

  81 volatile unsigned char synchronized = 0;

  82 volatile unsigned char syncQuality = 0; //0-31, 0=no useable signal, 15=medium quality, 31=perfect!

  83 volatile unsigned char syncCounter = 0;

  84 

  85 volatile unsigned char block = 0;

  86 volatile unsigned long bitstream = 0;

  87 volatile unsigned long bitstreamData = 0;

  88 volatile unsigned int block1 = 0;

  89 volatile unsigned int block2 = 0;

  90 volatile unsigned int block3 = 0;

  91 volatile unsigned int block4 = 0;

  92 

  93 //ISR is executed when there is a new rds bit to read

  94 ISR(INT0_vect) {

  95   cli();

  96   unsigned int syndrome = 0;

  97   

  98   //Copy global vars to local vars for performance

  99   unsigned long bitstreamLocal = bitstream;

 100   unsigned char blockLocal = block;

 101   

 102   //shift rds bit in on right side

 103   bitstreamLocal *= 2; 

 104   if (RDS_PORT & _BV(RDS_PIN))

 105     bitstreamLocal++;

 106     

 107   bitCounter++;

 108   

 109   //collect data for raw bitstream snapshots

 110   bitCounterSnapshot++;

 111   if (bitCounterSnapshot == 26) {

 112     bitstreamData = (bitstreamLocal & 0x3FFFFFF);

 113     bitCounterSnapshot = 0;

 114     

 115     event |= eventBitstreamSnapshot;

 116   }

 117   

 118   //when we have 26 bits or are not synchronized to the stream

 119   //check the CRC and maybe store a rds block

 120   if (bitCounter == 26 || !synchronized) {

 121   

 122     bitstreamLocal &= 0x3FFFFFF; //we only need 26 bits

 123   

 124     syndrome = (bitstreamLocal / 0x10000); //bits 16-31 (2^16)

 125   

 126     if (bitstreamLocal & _BV(0))

 127       syndrome ^= 0x031b;

 128     

 129     if (bitstreamLocal & _BV(1))

 130       syndrome ^= 0x038f;

 131     

 132     if (bitstreamLocal & _BV(2))

 133       syndrome ^= 0x02a7;

 134       

 135     if (bitstreamLocal & _BV(3))

 136       syndrome ^= 0x00f7;

 137     

 138     if (bitstreamLocal & _BV(4))

 139       syndrome ^= 0x01ee;

 140     

 141     if (bitstreamLocal & _BV(5))

 142       syndrome ^= 0x03dc;

 143     

 144     if (bitstreamLocal & _BV(6))

 145       syndrome ^= 0x0201;

 146     

 147     if (bitstreamLocal & _BV(7))

 148       syndrome ^= 0x01bb;

 149     

 150     if (bitstreamLocal & _BV(8))

 151       syndrome ^= 0x0376;

 152     

 153     if (bitstreamLocal & _BV(9))

 154       syndrome ^= 0x0355;

 155   

 156     if (bitstreamLocal & _BV(10))

 157       syndrome ^= 0x0313;

 158     

 159     if (bitstreamLocal & _BV(11))

 160       syndrome ^= 0x039f;

 161   

 162     if (bitstreamLocal & _BV(12))

 163       syndrome ^= 0x0287;

 164       

 165     if (bitstreamLocal & _BV(13))

 166       syndrome ^= 0x00b7;

 167     

 168     if (bitstreamLocal & _BV(14))

 169       syndrome ^= 0x016e;

 170 

 171     if (bitstreamLocal & 0b1000000000000000) // _BV(15) does not work!!!

 172       syndrome ^= 0x02dc;

 173 

 174     //Block A?

 175     if (blockLocal == 0 && syndrome == 0x03d8) {

 176       block1 = bitstreamLocal / 0x400; //bits 10-25 (2^10)

 177       synchronized = 1;

 178       blockLocal++;

 179     } else 

 180     //Block B?

 181     if (blockLocal == 1 && syndrome == 0x03d4) {

 182       block2 = bitstreamLocal / 0x400; //bits 10-25 (2^10)

 183       synchronized = 1;

 184       blockLocal++;

 185     } else  

 186     //Block C type A?

 187     if (blockLocal == 2 && !(block2 & _BV(11)) && syndrome == 0x025c) {

 188       block3 = bitstreamLocal / 0x400; //bits 10-25 (2^10)

 189       synchronized = 1;

 190       blockLocal++;

 191     } else

 192     //Block C type B?

 193     if (blockLocal == 2 && (block2 & _BV(11)) && syndrome == 0x03cc) {

 194       block3 = bitstreamLocal / 0x400; //bits 10-25 (2^10)

 195       synchronized = 1;

 196       blockLocal++;

 197     } else

 198     //Block D?

 199     if (blockLocal == 3 && syndrome == 0x0258) {

 200       block4 = bitstreamLocal / 0x400; //bits 10-25 (2^10)

 201       synchronized = 1;

 202       blockLocal = 0;

 203       //we have a complete group!

 204       event |= eventGroupComplete;

 205     } else {

 206       //sync lost..

 207       synchronized = 0;

 208       blockLocal = 0;

 209       event |= eventSyncLost;

 210     }

 211     

 212     bitCounter = 0; 

 213   }

 214   

 215   if (event & eventGroupComplete) {

 216     PORTC |= _BV(PC1);

 217     

 218     if (syncQuality < 31)

 219       syncQuality++;

 220     

 221   } else if (!synchronized) {

 222     PORTC &= ~_BV(PC1);

 223     

 224     syncCounter++;

 225     if (syncQuality > 0 && syncCounter == 26) {

 226       syncQuality--;

 227       syncCounter = 0;

 228     }

 229       

 230   }

 231   

 232   if (syncQuality >= 15)

 233     PORTC |= _BV(PC2);

 234   else

 235     PORTC &= ~_BV(PC2);

 236   

 237   //Store local vars into global vars to remember state

 238   bitstream = bitstreamLocal;

 239   block = blockLocal;

 240   sei();

 241 }

 242 

 243 //timer0 overflowed

 244 ISR(TIMER0_OVF_vect) {

 245   cli();

 246   timerOverflowCount++;

 247   if (timerOverflowCount > 4) {

 248     event |= eventTimerOverflow;

 249     timerOverflowCount = 0;

 250   }

 251   sei();

 252 }

 253 

 254 ISR(USART_RX_vect) {

 255   cli();

 256   usart0RxDByte = UDR0;

 257   event |= eventUsart0RxDInterrupt;

 258   sei();

 259 }

 260 

 261 void displayInfo(void) {

 262   printf_P(PSTR("INFO: 0x01, "));

 263   printf_P(PSTR(INFO));

 264   printf_P(PSTR("\r\n"));

 265 }

 266 

 267 int main(void) {

 268 

 269   wdt_reset(); //reset inmediately in case of a previous system reset

 270   //lets enable watchdog with 1 second timeout

 271   wdt_enable(WDTO_1S);

 272 

 273   unsigned int  programmeIdentificationCode = 0;

 274   unsigned char groupType = 0;

 275   unsigned char groupVersion = 0;

 276   unsigned char trafficProgrammeIdentificationCode = 0;

 277   unsigned char programmeTypeCode = 0;

 278   unsigned char trafficAnnouncementCode = 0;

 279   unsigned char musicSpeechSwitchScode = 0;

 280   unsigned char group0CodeBits = 0;

 281   unsigned char group0CodeBitsSteadyCount = 0;

 282   unsigned char programmeServiceName[8];

 283   unsigned char programmeServiceNameNew[8];

 284   unsigned char decoderIdentificationControlCode = 0;

 285   unsigned char decoderIdentificationControlCodeNew = 0;

 286   unsigned char alternativeFrequencyCodes[27];

 287   unsigned char syncMessage = 0;

 288   unsigned char displayRaw = 0;

 289   unsigned char displayBitstream = 0;

 290   unsigned char linkageActuator = 0;

 291   unsigned char extendedCountryCode = 0;

 292   unsigned char textSegmentAddress = 0;

 293   unsigned char textSegmentAddressPrevious = 0;

 294   unsigned char textVersion = 0;

 295   unsigned char textVersionPrevious = 0; 

 296   unsigned char radioText[64];

 297   unsigned char radioTextPrevious[64];

 298   unsigned char textSegmentAddress0Seen = 0;

 299   unsigned int  modifiedJulianDay = 0;

 300   unsigned int  utcYear = 0;

 301   unsigned char utcMonth = 0;

 302   unsigned char utcDay = 0;

 303   signed   char localHours = 0;

 304   unsigned char utcHours = 0;

 305   signed   char localMinutes = 0;

 306   unsigned char utcMinutes = 0;

 307   unsigned char utcMinutesPrevious = 0xFF;

 308   unsigned char localSign = 0;

 309   unsigned int  localTimeOffset = 0;

 310   

 311   //general purpose vars

 312   unsigned int  m;

 313   unsigned char h;

 314   unsigned char i;

 315   unsigned char j;

 316   

 317   //1.8mhz crystal

 318   //baudrate 115.2K

 319   //UBRR0 = 0;

 320   //baudrate 9600

 321   //UBRR0 = 11;

 322   

 323   //16mhz crystal

 324   //38.4k

 325   //UBRR0 = 25;

 326   

 327   //4.332Mhz crystal

 328   //baudrate 38K4

 329   UBRR0 = 6;

 330   

 331   //enable RxD interrupt, RxD, TxD

 332   UCSR0B |= _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0);

 333   

 334   //8 bit

 335   //UCSR0C |= _BV(UCSZ00) | _BV(UCSZ01);

 336   

 337   //UART is stdout and stdin;

 338   Uart = fdevopen(uartSend, uartRecv);

 339 

 340   //enable int0

 341   EIMSK = _BV(INT0);

 342   

 343   //INT0 raising edge

 344   EICRA |= _BV(ISC01) | _BV(ISC00);

 345   

 346   //PC0, PC1, PC2 are outputs;

 347   DDRC |= _BV(PC0) | _BV(PC1) | _BV(PC2);

 348 

 349   //timer0 prescaler clk/1024

 350   TCCR0B |= _BV(CS02) | _BV(CS00);

 351   //enable overflow interrupt

 352   TIMSK0 |= _BV(TOIE0);

 353   

 354   displayInfo();

 355   

 356   sei(); //enable interrupts;

 357   

 358   for (;;) {

 359     

 360     do {} while (!event);

 361     wdt_reset(); //reset watchdog, we are still alive!

 362     

 363     cli();

 364     if (event & eventUsart0RxDInterrupt) {

 365       event &= ~eventUsart0RxDInterrupt;

 366       sei();

 367       

 368       //collect the command

 369       

 370       //switch group display on (G) or off (g)

 371       if (usart0RxDByte == 'G')

 372         displayRaw = 0;

 373       else if (usart0RxDByte == 'g')

 374         displayRaw = 1;

 375         

 376       //reset decoder

 377       if (usart0RxDByte == 'r') {

 378         wdt_enable(WDTO_15MS);

 379         for(;;) {};

 380       }

 381         

 382       //switch group display on (B) or off (b)

 383       if (usart0RxDByte == 'B')

 384         displayBitstream = 0;

 385       else if (usart0RxDByte == 'b')

 386         displayBitstream = 1;

 387         

 388     } else {

 389       sei();

 390     }

 391     

 392     //we have got 26 bits raw rds data

 393     cli();

 394     if (event & eventBitstreamSnapshot) {

 395       event &= ~eventBitstreamSnapshot;

 396       sei();

 397       

 398       if (displayBitstream) {

 399         printf_P(PSTR("B: 0x%07lX\r\n"), bitstreamData);

 400       }

 401     } else {

 402       sei();

 403     }

 404     

 405     cli();

 406     if (event & eventTimerOverflow) {

 407       event &= ~eventTimerOverflow;

 408       sei();

 409       

 410       /* no rds signal message */

 411       if (syncQuality == 0 && syncMessage < 12) {

 412         syncMessage++;

 413         if (syncMessage == 12) {

 414           printf_P(PSTR("INFO: 0x02, No RDS signal.\r\n"));

 415           programmeIdentificationCode = 0;

 416         }

 417       }

 418           

 419       if (syncQuality > 0)

 420           syncMessage = 0;

 421           

 422       if (PORTC & _BV(PC0))

 423         PORTC &= ~_BV(PC0); //heartbeat led off

 424       else

 425         PORTC |= _BV(PC0); //heartbeat led on

 426       

 427     } else {

 428       sei();

 429     }

 430     

 431     //reset vars when sync lost. Warning, sync lost happens 26 times as many as groupcomlete, do not do anything lenghty here

 432     cli();

 433     if (event & eventSyncLost) {

 434       event &= ~eventSyncLost;

 435       sei();

 436       group0CodeBitsSteadyCount = 5;

 437     } else {

 438       sei();

 439     }

 440     

 441     cli();

 442     if (event & eventGroupComplete) {

 443         event &= ~eventGroupComplete;

 444         sei();

 445         

 446       //Group type code

 447       groupType = block2 / 0x1000; //bits 12-15 (2^12)

 448       

 449       //Group version code

 450       if (block2 & _BV(11))

 451         groupVersion = 'B';

 452       else

 453         groupVersion = 'A';

 454       

 455       if (displayRaw)

 456         printf_P(PSTR("GROUP%02u%c: 0x%04X 0x%04X 0x%04X 0x%04X\r\n"), groupType, groupVersion, block1, block2, block3, block4);

 457       

 458       //PI Codes of block 3 B-version groups are not decoded.

 459       

 460       //Programme Identification code

 461       if (programmeIdentificationCode != block1) {

 462         displayInfo();

 463         programmeIdentificationCode = block1;

 464         printf_P(PSTR("PI: 0x%04X, Detected new station.\r\n"), programmeIdentificationCode);

 465         

 466         /* reset variables because PI code changed */

 467         trafficProgrammeIdentificationCode = 0xFF;

 468         programmeTypeCode = 0xFF;

 469         trafficAnnouncementCode = 0xFF;

 470         musicSpeechSwitchScode = 0xFF;

 471   

 472         group0CodeBitsSteadyCount = 0;

 473         for(i = 0; i < sizeof(programmeServiceName); i++)

 474           programmeServiceName[i] = 0xFF;

 475           

 476         decoderIdentificationControlCode = 0xFF;

 477         

 478         for(i = 0; i < sizeof(alternativeFrequencyCodes); i++)

 479           alternativeFrequencyCodes[i] = 0;

 480           

 481         linkageActuator = 0xFF;

 482         

 483         extendedCountryCode = 0;

 484         

 485         for(i = 0; i < sizeof(radioText); i++) {

 486           radioText[i] = 0;

 487           radioTextPrevious[i] = 0;

 488         }

 489         

 490         textSegmentAddress0Seen = 0;

 491           

 492         textVersionPrevious = 0;

 493         textSegmentAddressPrevious = 0;

 494         

 495         utcMinutesPrevious = 0xFF;

 496         

 497       }

 498   

 499       //Programme Type code

 500       if (programmeTypeCode != ((block2 / 0x20) & 0x1F)) {

 501         programmeTypeCode = ((block2 / 0x20) & 0x1F); //bits 5-9 (2^5)

 502         printf_P(PSTR("PTY: 0x%02X, "), programmeTypeCode);

 503         switch(programmeTypeCode) {

 504           case 0:

 505             printf_P(PSTR("None."));

 506             break;

 507           case 1:

 508             printf_P(PSTR("News."));

 509             break;

 510           case 2:

 511             printf_P(PSTR("Current Affairs."));

 512             break;

 513           case 3:

 514             printf_P(PSTR("Information."));

 515             break;

 516           case 4:

 517             printf_P(PSTR("Sport."));

 518             break;

 519           case 5:

 520             printf_P(PSTR("Education."));

 521             break;

 522           case 6:

 523             printf_P(PSTR("Drama."));

 524             break;

 525           case 7:

 526             printf_P(PSTR("Cultures."));

 527             break;

 528           case 8:

 529             printf_P(PSTR("Science."));

 530             break;

 531           case 9:

 532             printf_P(PSTR("Varied Speech."));

 533             break;

 534           case 10:

 535             printf_P(PSTR("Pop Music."));

 536             break;

 537           case 11:

 538             printf_P(PSTR("Rock Music."));

 539             break;

 540           case 12:

 541             printf_P(PSTR("Easy Listening."));

 542             break;

 543           case 13:

 544             printf_P(PSTR("Light Classics."));

 545             break;

 546           case 14:

 547             printf_P(PSTR("Serious Classics."));

 548             break;

 549           case 15:

 550             printf_P(PSTR("Other Music."));

 551             break;

 552           case 16:

 553             printf_P(PSTR("Weather."));

 554             break;

 555           case 17:

 556             printf_P(PSTR("Finance."));

 557             break;

 558           case 18:

 559             printf_P(PSTR("Children."));

 560             break;

 561           case 19:

 562             printf_P(PSTR("Social Affairs."));

 563             break;

 564           case 20:

 565             printf_P(PSTR("Religion."));

 566             break;

 567           case 21:

 568             printf_P(PSTR("Phone In."));

 569             break;

 570           case 22:

 571             printf_P(PSTR("Travel & Touring."));

 572             break;

 573           case 23:

 574             printf_P(PSTR("Leisure & Hobby."));

 575             break;

 576           case 24:

 577             printf_P(PSTR("Jazz Music."));

 578             break;

 579           case 25:

 580             printf_P(PSTR("Country Music."));

 581             break;

 582           case 26:

 583             printf_P(PSTR("National Music."));

 584             break;

 585           case 27:

 586             printf_P(PSTR("Oldies Music."));

 587             break;

 588           case 28:

 589             printf_P(PSTR("Folk Music."));

 590             break;

 591           case 29:

 592             printf_P(PSTR("Documentary."));

 593             break;

 594           case 30:

 595             printf_P(PSTR("Alarm Test."));

 596             break;

 597           case 31:

 598             printf_P(PSTR("Alarm - Alarm !"));

 599             break;

 600           default:

 601             printf_P(PSTR("Unknown."));

 602             break;

 603         }

 604         printf_P(PSTR("\r\n"));

 605       }

 606   

 607       //Type 0 groups: Basic tuning and switching information

 608       if (groupType == 0) {

 609       

 610         //Traffic Programme Identification code

 611         //Traffic announcement code

 612         if (trafficAnnouncementCode != ((block2 / 0x10) & 0x01) ||

 613             trafficProgrammeIdentificationCode != (block2 & _BV(10)) / 0x400) {

 614           

 615           trafficAnnouncementCode = (block2 / 0x10) & 0x01; //bit 4 (2^4)

 616           trafficProgrammeIdentificationCode = (block2 & _BV(10)) / 0x400; //bit 10

 617           

 618           printf_P(PSTR("TP&TA: 0x%02X 0x%02X, "), trafficProgrammeIdentificationCode, trafficAnnouncementCode);

 619 

 620           if (trafficProgrammeIdentificationCode == 0) {

 621           

 622             if (trafficAnnouncementCode == 0) {

 623               printf_P(PSTR("No traffic announcements available."));

 624             } else {

 625               printf_P(PSTR("Traffic announcements available via EON on another station."));

 626             }

 627           

 628           } else {

 629           

 630             if (trafficAnnouncementCode == 0) {

 631               printf_P(PSTR("Traffic announcements available on this station and maybe via EON on another station."));

 632             } else {

 633               printf_P(PSTR("Traffic announcement in progress."));

 634             }

 635             

 636           }

 637           

 638           printf_P(PSTR("\r\n"));

 639           

 640         }

 641       

 642         //Music Speech switch code

 643         if (musicSpeechSwitchScode != ((block2 / 0x08) & 0x01)) {

 644           musicSpeechSwitchScode = (block2 / 0x08) & 0x01;  //bit 3 (2^3)

 645           printf_P(PSTR("MS: 0x%02X, "), musicSpeechSwitchScode);

 646 

 647           if (musicSpeechSwitchScode) 

 648             printf_P(PSTR("Music is being broadcasted or station does not use MS flag."));

 649           else

 650             printf_P(PSTR("Speech is being broadcasted."));

 651           printf_P(PSTR("\r\n"));

 652         }

 653         

 654         //Decode program service name and decoder identification control code

 655         group0CodeBits = block2 & 0x03; //0, 1, 2, 3;

 656         

 657         

 658         //TODO: improve decoderIdentificationControlCode detection, decouple from PS name

 659         //Decoder-identification control code-bit is bit 3 in block2

 660         if (group0CodeBits == 0) {

 661           if (block2 & 0x04)

 662             decoderIdentificationControlCodeNew |= _BV(3);

 663           else 

 664             decoderIdentificationControlCodeNew &= ~_BV(3);

 665         }

 666         if (group0CodeBits == 1) {

 667           if (block2 & 0x04)

 668             decoderIdentificationControlCodeNew |= _BV(2);

 669           else 

 670             decoderIdentificationControlCodeNew &= ~_BV(2);

 671         }

 672         if (group0CodeBits == 2) {

 673           if (block2 & 0x04)

 674             decoderIdentificationControlCodeNew |= _BV(1);

 675           else 

 676             decoderIdentificationControlCodeNew &= ~_BV(1);

 677         }

 678         if (group0CodeBits == 3) {

 679           if (block2 & 0x04)

 680             decoderIdentificationControlCodeNew |= _BV(0);

 681           else 

 682             decoderIdentificationControlCodeNew &= ~_BV(0);

 683         }

 684   

 685         //fill in information

 686         i = group0CodeBits * 2;

 687         programmeServiceNameNew[i] = block4 / 0xFF; //bits 8-16 (2^8)

 688         programmeServiceNameNew[i + 1] = block4; //bit 0-8

 689         

 690         if (programmeServiceNameNew[i] != programmeServiceName[i] ||

 691             programmeServiceNameNew[i + 1] != programmeServiceName[i + 1]) {

 692           //detected change, reset counter if not already counting

 693           if (group0CodeBitsSteadyCount > 4)

 694                 group0CodeBitsSteadyCount = 0;

 695         }

 696         

 697         //increase counter only when there are less then 4 received words

 698         if (group0CodeBitsSteadyCount < 4)

 699           group0CodeBitsSteadyCount++;

 700         

 701         programmeServiceName[i] = programmeServiceNameNew[i];

 702         programmeServiceName[i + 1] = programmeServiceNameNew[i + 1];

 703         

 704         //when we detected 4 new PS words then display it

 705         if (group0CodeBitsSteadyCount == 4) {

 706           printf_P(PSTR("PS: "));

 707           for(i = 0; i < sizeof(programmeServiceName); i++) {

 708             printf_P(PSTR("%c"), programmeServiceName[i]);

 709           }

 710           printf_P(PSTR("\r\n"));

 711           //prevent redisplay

 712           group0CodeBitsSteadyCount++;

 713           

 714           if (decoderIdentificationControlCode != decoderIdentificationControlCodeNew) {

 715             decoderIdentificationControlCode = decoderIdentificationControlCodeNew;

 716             printf_P(PSTR("DI: 0x%02X"), decoderIdentificationControlCode);

 717             

 718             if (decoderIdentificationControlCode & 0b0001)

 719               printf_P(PSTR(", Stereo"));

 720             else

 721               printf_P(PSTR(", Mono"));

 722               

 723             if (decoderIdentificationControlCode & 0b0010)

 724               printf_P(PSTR(", Artificial Head"));

 725               

 726             if (decoderIdentificationControlCode & 0b0100)

 727               printf_P(PSTR(", Compressed"));

 728               

 729             if (decoderIdentificationControlCode & 0b1000)

 730               printf_P(PSTR(", Static PTY"));

 731             else

 732               printf_P(PSTR(", Dynamic PTY"));

 733               

 734             printf_P(PSTR(".\r\n"));

 735           }

 736         }

 737         

 738   

 739         if (groupVersion == 'A') {

 740         

 741           //Alternative frequency codes

 742           for (h = 0; h < 2; h++) {

 743             if (h == 0)

 744               j = block3; //first AF is in bits 0-7 of block3

 745             else

 746               j = block3 / 256; //second bits 8-15

 747               

 748             //only frequencies we want, no control codes

 749             if (j >= 1 && j <= 204) {

 750               for(i = 0; i < sizeof(alternativeFrequencyCodes); i++) {

 751                 if (alternativeFrequencyCodes[i] == j) {

 752                   break;

 753                 } 

 754                 if (alternativeFrequencyCodes[i] == 0) {

 755                   alternativeFrequencyCodes[i] = j;

 756                   printf_P(PSTR("AF: 0x%02X, "), alternativeFrequencyCodes[i]);

 757                   

 758                   m = 875 + j;

 759                   printf_P(PSTR("%u.%uMHz.\r\n"), m / 10, m % 10);

 760                   break;

 761                 }

 762               }

 763             }

 764             

 765             if (j == 224 && alternativeFrequencyCodes[25] != j) {

 766               alternativeFrequencyCodes[25] = 224;

 767               printf_P(PSTR("AF: 0x%02X, This station has no alternate frequenties.\r\n"), j);

 768             }

 769           }

 770           

 771           //TODO: LF/MF untested because lack of LM/MF station

 772           //Station transmits on LF/MF

 773           if (block3 == 250 && alternativeFrequencyCodes[26] != block3) {

 774             alternativeFrequencyCodes[26] = block3;

 775             j = block3 / 256;

 776             

 777             printf_P(PSTR("AF: 0x%02X 0x%02X, "), block3, j);

 778             

 779             //LF 153kHz-279kHz in 9 KHz steps

 780             if (j >= 1 && j <= 15) {

 781               m = 144 + (j * 9);

 782               printf_P(PSTR("%uKHz.\r\n"), m);

 783             } else

 784             //MF 531KHz-1602kHz in 9 KHz steps

 785             if (j >= 16 && j <= 135) {

 786               m = 387 + (j * 9);

 787               printf_P(PSTR("%uKHz.\r\n"), m);

 788             }

 789           }

 790         }

 791         //version B contains PI code in block3

 792   

 793       } else

 794       //Type 1 groups: Programme Item Number and slow labelling codes

 795       if (groupType == 1) {

 796       

 797         if (groupVersion == 'A') {

 798           //TODO: 5 bit Radio Paging Codes, bits 0-4 block2

 799           

 800           //TODO: Add textual description of LA

 801           //Linkage Actuator

 802           if (linkageActuator != (block3 & _BV(15))) {

 803             linkageActuator = (block3 & _BV(15));

 804             printf_P(PSTR("LA: 0x%02X\r\n"), linkageActuator);

 805           }

 806           

 807           //store variant code, bits 13-15

 808           i = (block3 / 0x2000) & 0x07;

 809           

 810           //TODO: Paging

 811           if (i == 0) {

 812             

 813             //TODO: Add textual description of ECC code

 814             //Extended Country Code, bits 0-7 block3

 815             if (extendedCountryCode != (block3 & 0xFF)) {

 816               extendedCountryCode = (block3 & 0xFF);

 817               printf_P(PSTR("ECC: 0x%02X\r\n"), extendedCountryCode);

 818             }

 819             

 820           

 821           } else 

 822           //TODO: TMC identification

 823           if (i == 1) {

 824           

 825           } else

 826           //TODO: Paging identification

 827           if (i == 2) {

 828           

 829           } else

 830           //TODO: Language codes

 831           if (i == 3) {

 832           

 833           } else

 834           //TODO: not assigned

 835           if (i == 4) {

 836           

 837           } else

 838           //TODO: not assigned

 839           if (i == 5) {

 840           

 841           } else

 842           //TODO: For use by broadcasters

 843           if (i == 6) {

 844           

 845           } else

 846           //TODO: Identification of EWS channel

 847           if (i == 7) {

 848           

 849           }

 850         }

 851         

 852         //TODO: Programme Item Number, block 4

 853       

 854       } else 

 855       //Type 2 groups: RadioText

 856       if (groupType == 2) {

 857       

 858         //text version A or B, bit 5 block 2

 859         if (block2 & 0x10) 

 860           textVersion = 'B';

 861         else

 862           textVersion = 'A';

 863         

 864         //block2 bit 0-3

 865         textSegmentAddress = (block2 & 0x0F);

 866         

 867         //clean radioText when version changes

 868         if (textVersionPrevious != 0 &&

 869             textVersionPrevious != textVersion) {

 870             

 871           for(i = 0; i < sizeof(radioText); i++) {

 872             radioText[i] = 0;

 873            }

 874            

 875           textSegmentAddressPrevious = 0;

 876           textSegmentAddress0Seen = 0;

 877         }

 878         

 879         //detected new start of text segment, normally address 0x00

 880         if (textSegmentAddressPrevious > textSegmentAddress) {

 881             

 882           if (groupVersion == 'A')

 883             h = 64;

 884           else

 885             h = 32;

 886           

 887           //detect new radioText

 888           j = 0;

 889           for (i = 0; i < h; i++) {

 890             if (radioText[i] != 0 && radioText[i] != ' ') {

 891               if (radioText[i] != radioTextPrevious[i]) {

 892                 j = 1;

 893                 break;

 894               }

 895             }

 896           }

 897           

 898           //only print when we have received address 0 once.

 899           if (textSegmentAddress0Seen == 0)

 900             j = 0;

 901            

 902           if (j) {

 903             printf_P(PSTR("RT%c: "), textVersion);

 904 

 905             for (i = 0; i < h; i++) {

 906             

 907               if (radioText[i] == 0) 

 908                 break;

 909               else if (!(radioText[i] == '\r' || radioText[i] == '\n'))

 910                 printf_P(PSTR("%c"), radioText[i]);

 911 

 912                 

 913               radioTextPrevious[i] = radioText[i];

 914             }

 915             printf_P(PSTR("\r\n"));

 916           }

 917           

 918           

 919         }

 920         

 921         //64 bit messages in block 3 & 4

 922         if (groupVersion == 'A') {

 923           

 924           radioText[(textSegmentAddress * 4)] = block3 / 256;

 925           radioText[(textSegmentAddress * 4) + 1] = block3;

 926           radioText[(textSegmentAddress * 4) + 2] = block4 / 256;

 927           radioText[(textSegmentAddress * 4) + 3] = block4;

 928           

 929           //fill bytes smaller then textSegmentAddress with spaces when they are '0'

 930           if (textSegmentAddress > 0) {

 931             for (i = 0; i < (textSegmentAddress - 1) * 4; i++) {

 932               if (radioText[i] == 0)

 933                 radioText[i] = ' ';

 934             }

 935           }

 936           

 937         }

 938         //TODO: 32 bit messages not tested because of lack station transmitting it.

 939         //32 bit messages in block 4

 940         else {

 941           radioText[(textSegmentAddress * 2)] = block4 / 256;

 942           radioText[(textSegmentAddress * 2) + 1] = block4;

 943           

 944           //fill bytes smaller then textSegmentAddress with spaces when they are '0'

 945           if (textSegmentAddress > 0) {

 946             for (i = 0; i < (textSegmentAddress - 1) * 2; i++) {

 947               if (radioText[i] == 0)

 948                 radioText[i] = ' ';

 949             }

 950           }

 951           

 952         }

 953         

 954         if (textSegmentAddress == 0)

 955           textSegmentAddress0Seen = 1;

 956         

 957         

 958         textVersionPrevious = textVersion;

 959         textSegmentAddressPrevious = textSegmentAddress;

 960         

 961       } else

 962       //Type 3A groups: Application identification for Open data

 963       if (groupType == 3 && groupVersion == 'A') {

 964       

 965       } else

 966       //Type 3B groups: Open Data Application

 967       if (groupType == 3 && groupVersion == 'B') {

 968       

 969       } else

 970       //Type 4A groups : Clock-time and date

 971       if (groupType == 4 && groupVersion == 'A') {

 972       

 973         //bits 0-5 are in block4 as bits 6-11

 974         utcMinutes = (block4  / 64) & 0x3F;

 975       

 976         if (utcMinutesPrevious != utcMinutes) {

 977         

 978           utcMinutesPrevious = utcMinutes;

 979         

 980           //bits 0-14 are in block3 as bits 1-15

 981           //bits 15-16 are in block2 as bits 0-1

 982           modifiedJulianDay = (block3 / 2) + (block2 & 0x03) * 32768;

 983           

 984           //bits 0-3 are in block4 as bits 12-15

 985           //bit 4 is in block3 as bit 1

 986           utcHours = (block4 / 4096) + (block3 & 0x01) * 16;

 987           

 988           //local time offset are bits 0-4 in block 4

 989           localTimeOffset = block4 & 0x1F;

 990           //sign is in bit 5 of block4, 0=+ 1=-

 991           if (block4 & 0x20) 

 992             localSign = '-';

 993           else

 994             localSign = '+';

 995           

 996           //multiply by 30 so that we have offset in minutes (offset is in multiples of .5 hours)

 997           localTimeOffset *= 30;

 998   

 999           printf_P(PSTR("CT: 0x%01X%04X%04X, "), (block2 & 0x03), block3, block4);

1000           

1001           //Modified Julian date to year-month-day conversion

1002           utcYear = floor((modifiedJulianDay - 15078.2) / 365.25);

1003           utcMonth = floor((modifiedJulianDay - 14956.1 - floor(utcYear * 365.25)) / 30.6001);

1004           utcDay = modifiedJulianDay - 14956 - floor(utcYear * 365.25) - floor(utcMonth * 30.6001);

1005         

1006           if (utcMonth == 14 || utcMonth == 15)

1007             i = 1; 

1008           else 

1009             i = 0;

1010         

1011           utcYear = utcYear + i + 1900;

1012           utcMonth = utcMonth - 1 - (i * 12);

1013   

1014           printf_P(PSTR("UTC %04u-%02u-%02u (MJD %u) %02u:%02u:00 %c%02u:%02u, "),

1015                    utcYear, utcMonth, utcDay,

1016                    modifiedJulianDay,

1017                    utcHours, utcMinutes, localSign,

1018                    localTimeOffset / 60, localTimeOffset % 60);

1019           

1020           //TODO: half hour timezones and negative timezones not tested because lack of station transmitting it.

1021           //lets calulate local time

1022           if (localSign == '-') {

1023             localHours = utcHours - (localTimeOffset / 60);

1024             localMinutes = utcMinutes - (localTimeOffset % 60);

1025           } else {

1026             localHours = utcHours + (localTimeOffset / 60);

1027             localMinutes = utcMinutes + (localTimeOffset % 60);

1028           }

1029           

1030           if (localMinutes < 0) {

1031             localMinutes += 60;

1032             localHours--;

1033           }

1034             

1035           if (localMinutes > 59) {

1036             localMinutes -= 60;

1037             localHours++;

1038           }

1039           

1040           if (localHours < 0)

1041             localHours += 24;

1042   

1043           if (localHours > 23)

1044             localHours -= 24;

1045             

1046           printf_P(PSTR("TIME %02u:%02u:00\r\n"), localHours, localMinutes);

1047         }

1048 

1049       } else

1050       //Type 4B groups: Open data application

1051       if (groupType == 4 && groupVersion == 'B') {

1052         

1053       } else

1054       //Type 5 groups: Transparent data channels or ODA

1055       if (groupType == 5) {

1056       

1057       } else

1058       //Type 6 groups: In-house applications or ODA

1059       if (groupType == 6) {

1060       

1061       } else 

1062       //Type 7A groups: Radio Paging or ODA

1063       if (groupType == 7 && groupVersion == 'A') {

1064       

1065       } else 

1066       //Type 7B groups: Open data application

1067       if (groupType == 7 && groupVersion == 'B') {

1068       

1069       } else 

1070       //Type 8 groups: Traffic Message Channel or ODA

1071       if (groupType == 8) {

1072       

1073       } else 

1074       //Type 9 groups: Emergency warning systems or ODA

1075       if (groupType == 9) {

1076       

1077       } else 

1078       //Type 10A groups: Programme Type Name

1079       if (groupType == 10 && groupVersion == 'A') {

1080       

1081       } else

1082       //Type 10B groups: Open data

1083       if (groupType == 10 && groupVersion == 'A') {

1084       

1085       } else

1086       //Type 11 groups: Open Data Application

1087       if (groupType == 11) {

1088       

1089       } else 

1090       //Type 12 groups: Open Data Application

1091       if (groupType == 12) {

1092       

1093       } else 

1094       //Type 13A groups: Enhanced Radio Paging or ODA

1095       if (groupType == 13 && groupVersion == 'A') {

1096       

1097       } else

1098       //Type 13B groups: Open Data Application

1099       if (groupType == 13 && groupVersion == 'B') {

1100       

1101       } else

1102       //Type 14 groups: Enhanced Other Networks information

1103       if (groupType == 14) {

1104       

1105       } else

1106       //Type 15A groups: 'currently unavailable'

1107       if (groupType == 15 && groupVersion == 'A') {

1108       

1109       } else

1110       //Type 15B groups: Fast basic tuning and switching information

1111       if (groupType == 15 && groupVersion == 'B') {

1112       

1113       }

1114     } else {

1115       sei();

1116     }

1117     

1118     

1119   }

1120 }

 

 

你可能感兴趣的:(工作)