话说西元一千九百七十四年,欧洲有一帮不安分份子开始密谋策划,妄想利用有限的无线电频谱资源,去传送更多的资信讯息。六年后的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源代码解码:
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 #include42 #include 43 #include 44 #include 45 #include 46 #include 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 }