2 * Copyright (C) 2009, 2010 by Nils Faerber <nils.faerber@kernelconcepts.de>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
23 #include "rds_consts.h"
29 static struct rds_info_s rds_info;
30 static struct tm rds_time;
32 static struct _RDS_Private {
33 void (*rds_PI_cb)(unsigned short PI, unsigned char ccode, unsigned char ptype, unsigned char pref, void *user_data);
35 void (*rds_sname_cb)(char *sname, void *user_data);
36 void *rds_sname_cb_data;
37 void (*rds_sinfo_cb)(unsigned char TP, unsigned char TA, unsigned char MS, unsigned char PTY, void *user_data);
38 void *rds_sinfo_cb_data;
39 void (*rds_radiotext_cb)(char *radio_text, void *user_data);
40 void *rds_radiotext_cb_data;
41 void (*rds_date_time_cb)(struct tm *dtime, void *user_data);
42 void *rds_date_time_cb_data;
43 void (*rds_afinfo_cb)(unsigned char cnt, double *AF, void *user_data);
44 void *rds_afinfo_cb_data;
48 memset(&_rds_private, 0, sizeof(struct _RDS_Private));
51 void rds_set_PI_cb(void (*rds_PI_cb)(unsigned short PI, unsigned char ccode, unsigned char ptype, unsigned char pref, void *user_data), void *user_data)
53 _rds_private.rds_PI_cb = rds_PI_cb;
54 _rds_private.rds_PI_cb_data = user_data;
57 void rds_set_sname_cb(void (*rds_sname_cb)(char *sname, void *user_data), void *user_data)
59 _rds_private.rds_sname_cb = rds_sname_cb;
60 _rds_private.rds_sname_cb_data = user_data;
63 void rds_set_sinfo_cb(void (*rds_sinfo_cb)(unsigned char TP, unsigned char TA, unsigned char MS, unsigned char PTY, void *user_data), void *user_data)
65 _rds_private.rds_sinfo_cb = rds_sinfo_cb;
66 _rds_private.rds_sinfo_cb_data = user_data;
69 void rds_set_afinfo_cb(void (*rds_afinfo_cb)(unsigned char cnt, double *AF, void *user_data), void *user_data)
71 _rds_private.rds_afinfo_cb = rds_afinfo_cb;
72 _rds_private.rds_afinfo_cb_data = user_data;
75 void rds_set_radiotext_cb(void (*rds_radiotext_cb)(char *radio_text, void *user_data), void *user_data)
77 _rds_private.rds_radiotext_cb = rds_radiotext_cb;
78 _rds_private.rds_radiotext_cb_data = user_data;
81 void rds_set_date_time_cb(void (*rds_date_time_cb)(struct tm *dtime, void *user_data), void *user_data)
83 _rds_private.rds_date_time_cb = rds_date_time_cb;
84 _rds_private.rds_date_time_cb_data = user_data;
88 int rds_receive_group(int rds_fd, unsigned short *rdsgroup)
90 static unsigned char expected = 0;
92 unsigned char rbuf[3];
96 rb = read(rds_fd, rbuf, 3);
100 printf("#read err rb=%d\n", rb);
103 block = rbuf[0] | (rbuf[1] << 8);
104 offset = rbuf[2] & 0x07;
106 if (rbuf[2] & 0x80) {
107 if (OutputFlags & RDS_RECEIVE_INDICATOR)
110 // printf("group block=0x%04x offs=0x%02x\n", block, offset);
111 if (offset != expected) {
112 if (OutputFlags & RDS_RECEIVE_INDICATOR)
113 printf("block out of sync - resetting\n");
115 memset(rdsgroup, 0, sizeof(rdsgroup));
117 rdsgroup[offset] = block;
121 if (OutputFlags & RDS_RECEIVE_INDICATOR)
127 if (OutputFlags & RDS_RECEIVE_INDICATOR)
134 enum RDSGroupType { GROUP_0A=0, GROUP_0B, GROUP_1A, GROUP_1B, GROUP_2A, GROUP_2B,
135 GROUP_3A, GROUP_3B, GROUP_4A, GROUP_4B, GROUP_5A, GROUP_5B,
136 GROUP_6A, GROUP_6B, GROUP_7A, GROUP_7B, GROUP_8A, GROUP_8B,
137 GROUP_9A, GROUP_9B, GROUP_10A, GROUP_10B, GROUP_11A, GROUP_11B,
138 GROUP_12A, GROUP_12B, GROUP_13A, GROUP_13B, GROUP_14A, GROUP_14B,
139 GROUP_15A, GROUP_15B, GROUP_UNKNOWN };
141 void rds_radio_retuned(void)
143 memset(&rds_info, 0, sizeof(rds_info));
145 memset(&rds_time, 0, sizeof(rds_time));
148 void rds_decode_group(unsigned short *rdsgroup)
150 static unsigned short ogrp[4];
151 static unsigned char grp_decoded = 0;
152 static unsigned char sname_rcvd = 0;
153 unsigned char grp_type = (rdsgroup[1] >> 11);
155 static unsigned char otextAB = 0, newtext = 0;
156 unsigned short PI = rdsgroup[0];
157 unsigned char textAB = 0;
158 int local_time_off = 0;
162 /* we want to wait for at least two identical group transmits */
163 if (ogrp[0] != rdsgroup[0] || ogrp[1] != rdsgroup[1] || ogrp[2] != rdsgroup[2] || ogrp[3] != rdsgroup[3]) {
164 ogrp[0] = rdsgroup[0];
165 ogrp[1] = rdsgroup[1];
166 ogrp[2] = rdsgroup[2];
167 ogrp[3] = rdsgroup[3];
168 //printf("grp: 0x%04x 0x%04x 0x%04x 0x%04x\n", rdsgroup[0], rdsgroup[1], rdsgroup[2], rdsgroup[3]);
175 if (rds_info.PI != PI) {
176 /* station change? */
177 memset(&rds_info, 0, sizeof(rds_info));
179 memset(&rds_time, 0, sizeof(rds_time));
180 rds_info.ccode = (PI & 0xf000) >> 12;
181 rds_info.ptype = (PI & 0x0f00) >> 8;
182 rds_info.pref = (PI & 0x00ff);
184 if (rds_info.pref == 0) /* something is wrong here */
186 rds_info.PI = rdsgroup[0];
187 if (_rds_private.rds_PI_cb != NULL)
188 _rds_private.rds_PI_cb(rds_info.PI, rds_info.ccode, rds_info.ptype, rds_info.pref, _rds_private.rds_PI_cb_data);
189 if (OutputFlags & RDS_OUTPUT_RDSINFO)
190 printf("PI=%d ccode=%X ptype=%X '%s' '%s' pref=%d\n", rds_info.PI, rds_info.ccode, rds_info.ptype, ptype_stext[rds_info.ptype], ptype_ltext[rds_info.ptype], rds_info.pref);
194 case GROUP_0A: { /* basic switching and tuning */
195 unsigned char AFc1, AFc2;
198 offs = (rdsgroup[1] & 0x03);
201 if (offs == 1 && sname_rcvd == 0)
203 if (offs == 2 && sname_rcvd == 1)
205 if (offs == 3 && sname_rcvd == 2)
207 rds_info.sname[offs*2] = ((rdsgroup[3] & 0xff00) >> 8);
208 rds_info.sname[(offs*2)+1] = rdsgroup[3] & 0x00ff;
209 if (_rds_private.rds_sname_cb != NULL && sname_rcvd == 3) {
210 _rds_private.rds_sname_cb(rds_info.sname, _rds_private.rds_sname_cb_data);
214 rds_info.TA = (rdsgroup[1] & 0x10) >> 4;
215 rds_info.TP = (rdsgroup[1] & 0x400) >> 10;
216 rds_info.MS = (rdsgroup[1] & 0x08) >> 3;
217 rds_info.PTY = (rdsgroup[1] & 0x3e0) >> 5;
218 if (_rds_private.rds_sinfo_cb != NULL)
219 _rds_private.rds_sinfo_cb(rds_info.TP, rds_info.TA, rds_info.MS, rds_info.PTY, _rds_private.rds_sinfo_cb_data);
221 AFc1 = ((rdsgroup[2] & 0xff00) >> 8);
222 AFc2 = (rdsgroup[2] & 0x00ff);
223 if (AFc1 > 225 && AFc1 < 250) {
224 if (OutputFlags & RDS_OUTPUT_STATION_ID)
225 printf(" %d AFs follow:\n", AFc1-224);
226 } else if (AFc1 > 0 && AFc1 < 205)
227 AF1 = (float)87.5 + ((float)AFc1 * .1);
228 else if (AFc1 == 205)
229 printf(" filler- \n");
230 if (AFc2 > 0 && AFc2 < 205)
231 AF2 = (float)87.5 + ((float)AFc2 * .1);
233 if (OutputFlags & RDS_OUTPUT_STATION_ID)
234 printf("sname = '%s' %s %s %s AF1=%3.2f AF2=%3.2f PTY='%s'\n", rds_info.sname, rds_info.TP ? "TP" : "", rds_info.TA ? "TA" : "", rds_info.MS ? "M" : "S", AF1, AF2, PTY_text[rds_info.PTY]);
238 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
239 printf("group 0B\n");
241 case GROUP_1A: { /* Programme Item Number and slow labelling codes */
242 unsigned char variant, day, hour, minute;
243 unsigned short tmc_id;
245 variant = (rdsgroup[2] & (0x07 << 12)) >> 12;
246 day = (rdsgroup[3] & (0x1f << 11)) >> 11;
247 hour = (rdsgroup[3] & (0x1f << 6)) >> 6;
248 minute = (rdsgroup[3] & 0x3f);
249 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
250 printf("1A var=%d day=%d %d:%d ", variant, day, hour, minute);
252 rds_info.ECC = (rdsgroup[2] & 0xff);
253 printf(" ccode=%d ECC=0x%02x '%s' ", rds_info.ccode, rds_info.ECC, ECC_text[((rds_info.ECC & 0x0f)*16)+(rds_info.ccode-1)]);
256 tmc_id = (rdsgroup[2] & 0x0fff);
257 printf(" TMC ID=0x%04x", tmc_id);
260 printf(" Paging ID=0x%04x", (rdsgroup[2] & 0x0fff));
263 rds_info.lang_code = (rdsgroup[2] & 0xff);
264 printf(" language=0x%02x ", rds_info.lang_code);
265 switch (rds_info.lang_code) {
293 printf(" EWS Channel ID=0x%04x", (rdsgroup[2] & 0x0fff));
298 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
299 printf("group 1B\n");
301 case GROUP_2A: /* 2A has 64 chars, 4 per group */
302 case GROUP_2B: /* 2B has 32 chars, 2 per group */
303 offs = (rdsgroup[1] & 0x0f);
304 textAB = (rdsgroup[1] & 0x10);
305 if (textAB != otextAB) {
306 memset(rds_info.rtext, ' ', 64);
307 rds_info.rtext[64] = '\0';
310 if (grp_type == GROUP_2A) {
311 rds_info.rtext[offs*4] = ((rdsgroup[2] & 0xff00) >> 8);
312 rds_info.rtext[(offs*4)+1] = (rdsgroup[2] & 0x00ff);
313 rds_info.rtext[(offs*4)+2] = ((rdsgroup[3] & 0xff00) >> 8);
314 rds_info.rtext[(offs*4)+3] = (rdsgroup[3] & 0x00ff);
316 rds_info.rtext[offs*2] = ((rdsgroup[3] & 0xff00) >> 8);
317 rds_info.rtext[(offs*2)+1] = (rdsgroup[3] & 0x00ff);
319 if (offs == 0) /* new text always starts at 0 */
321 if ((offs == 15)) { /* all parts received */
323 if (_rds_private.rds_radiotext_cb != NULL)
324 _rds_private.rds_radiotext_cb(rds_info.rtext, _rds_private.rds_radiotext_cb_data);
325 if (OutputFlags & RDS_OUTPUT_RADIO_TEXT)
326 printf("RT '%s'\n", rds_info.rtext);
331 case GROUP_3A: /* ODA - TMC alarm, */
332 rds_info.AID = rdsgroup[3];
334 if (rds_info.AID == 0xcd46 || rds_info.AID == 0x0d45) {
335 if ((rdsgroup[2] & 0xc000) == 0) {
336 rds_info.LTN = (rdsgroup[2] & 0x0fc0) >> 6;
337 rds_info.AFI = (rdsgroup[2] & 0x0020) >> 5;
338 rds_info.M = (rdsgroup[2] & 0x0010) >> 4;
339 rds_info.I = (rdsgroup[2] & 0x0008) >> 3;
340 rds_info.N = (rdsgroup[2] & 0x0004) >> 2;
341 rds_info.R = (rdsgroup[2] & 0x0002) >> 1;
342 rds_info.U = (rdsgroup[2] & 0x0001);
344 if (rdsgroup[2] & 0x4000) {
347 rds_info.G = (rdsgroup[2] & 0x3000) >> 12;
348 rds_info.Ta = (rdsgroup[2] & 0x0030) >> 4;
349 rds_info.Tw = (rdsgroup[2] & 0x000c) >> 2;
350 rds_info.Td = (rdsgroup[2] & 0x0003);
354 if (OutputFlags & RDS_OUTPUT_RDSINFO) {
355 printf("AID = 0x%04x\n", rds_info.AID);
356 printf("LTN ID = %d (0x%02x)\n", rds_info.LTN, rds_info.LTN);
357 printf("Traffic information: ");
359 printf("international ");
368 } else if (rds_info.AID == 0x4bd7) {
369 if (OutputFlags & RDS_OUTPUT_RDSINFO) {
370 printf("RT+\nG2 = 0x%04x\n", rdsgroup[2]);
371 printf("template = 0x%02x\n", (rdsgroup[2] & 0x00ff));
372 printf("SCB = 0x%02x\n", (rdsgroup[2] & 0x0f00)>>8);
373 printf("CB = 0x%02x\n", (rdsgroup[2] & 0x1000)>>12);
374 printf("rfu = 0x%02x\n", (rdsgroup[2] & 0xe000)>>13);
377 printf("3A AID=0x%04x\n", rds_info.AID);
381 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
382 printf("group 3B\n");
385 //printf("group 4A - time/date\n");
386 rds_time.tm_sec = 0; // date/time message is sent at 00 secs +/- .2sec
387 rds_time.tm_min = ((rdsgroup[3] & (0x3f << 6)) >> 6);
388 rds_time.tm_hour = ((rdsgroup[3] & (0x0f << 12)) >> 12) | ((rdsgroup[2] & 0x01) << 4);
389 local_time_off = (rdsgroup[3] & 0x0f) * ((rdsgroup[3] & 0x10) ? -1 : 1);
390 rds_time.tm_hour += local_time_off / 2;
391 rds_time.tm_min = (rds_time.tm_min + ((local_time_off % 2) * 30)) % 60;
393 day_code = (((rdsgroup[2] & 0xfffe) >> 1) | ((rdsgroup[1] & 0x03) << 15)) /*+ local_time_off*/;
395 year_ = ((float)day_code - 15078.2) / 365.25;
396 mon_ = ((day_code - 14956.1) - (int)(year_ * 365.25)) / 30.6001;
397 rds_time.tm_mday = day_code - 14956 - (int)(year_ * 365.25) - (int)(mon_ * 30.6001);
398 if (mon_ == 14 || mon_ == 15)
402 rds_time.tm_year = year_ + K;
403 rds_time.tm_mon = mon_ - 1 - (K * 12) - 1;
405 rds_time.tm_isdst = -1;
407 if (_rds_private.rds_date_time_cb != NULL)
408 _rds_private.rds_date_time_cb(&rds_time, _rds_private.rds_date_time_cb_data);
410 if (OutputFlags & RDS_OUTPUT_DATETIME)
411 printf("%s", asctime(&rds_time));
413 printf("%d:%02d - local time offset = %d, %s (dcode = %d)\n", rds_time.tm_hour, rds_time.tm_min, local_time_off, asctime(&rds_time), day_code);
415 //printf("day=%d month=%d year=%d\n", day, mon, year);
418 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
422 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
426 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
430 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
434 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
438 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
442 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
446 if (rds_info.LTN != 0) // non TMCpro
447 decode_tmc(rdsgroup);
448 if (rds_info.LTN == 0)
452 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
456 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
460 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
464 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
468 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
471 case GROUP_11A: /* Open Data Application ODA */
472 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
473 printf("GRP11A ODA: #1=0x%02x #2=0x%04x #3=0x%04x\n", rdsgroup[1] & 0x1f, rdsgroup[2], rdsgroup[3]);
475 /* we previously got an RT+ identifier, try RT+ decoding */
476 if (rds_info.AID == 0x4bd7) {
477 printf("RT+\ntoggle: %s\n", (rdsgroup[1] & 0x10) ? "yes" : "no");
478 printf("item running : %s\n", (rdsgroup[1] & 0x08) ? "yes" : "no");
479 printf("content type 1 : %d\n", (rdsgroup[1] & 0x07) << 3 | (rdsgroup[2] & 0x0e00) >> 13);
480 printf("start marker 1 : %d\n", (rdsgroup[2] & 0x1f80) >> 7);
481 printf("length marker 1: %d\n", (rdsgroup[2] & 0x007e) >> 1);
482 printf("content type 2 : %d\n", (rdsgroup[1] & 0x01) << 4);
483 printf("start marker 2 : %d\n", (rdsgroup[2] & 0x07e0) >> 5);
484 printf("length marker 2: %d\n", (rdsgroup[2] & 0x001f));
487 case GROUP_11B: /* Open Data Application ODA */
488 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
490 printf("11B ODA: #1=0x%02x PI=0x%04x #3=0x%04x\n", rdsgroup[1] & 0x1f, rdsgroup[2], rdsgroup[3]);
492 case GROUP_12A: /* Open Data Application ODA */
493 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
496 case GROUP_12B: /* Open Data Application ODA */
497 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
500 case GROUP_13A: /* Open Data Application ODA or paging */
501 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
504 case GROUP_13B: /* Open Data Application ODA */
505 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
508 case GROUP_14A: /* EON */ {
509 unsigned char variant = rdsgroup[1] & 0x0f;
510 unsigned char AFc1=0, AFc2=0;
514 rds_info.PTY = (rdsgroup[1] & 0x3e0) >> 5;
515 rds_info.TPTN = (rdsgroup[1] & 0x400) >> 10;
516 rds_info.TPON = (rdsgroup[1] & 0x10) >> 4;
519 if (variant <= 3) { // PS
520 rds_info.PS[variant*2] = ((rdsgroup[2] & 0xff00) >> 8);
521 rds_info.PS[(variant*2)+1] = (rdsgroup[2] & 0x00ff);
522 } else if (variant == 4) {
523 printf("EON variant 4\n");
524 } else if (variant >= 5 && variant <= 8) { // AF - alternate frequency
525 AFc1 = ((rdsgroup[2] & 0xff00) >> 8);
526 AFc2 = (rdsgroup[2] & 0x00ff);
527 if (AFc1 > 0 && AFc1 < 205)
528 AF1 = (float)87.5 + ((float)AFc1 * .1);
529 else if (AFc1 >= 225 && AFc1 <= 249)
531 if (AFc2 > 0 && AFc2 < 205)
532 AF2 = (float)87.5 + ((float)AFc2 * .1);
533 else if (AFc2 >= 225 && AFc2 <= 249)
536 if (OutputFlags & RDS_OUTPUT_EON) {
537 printf("EON var=0x%01x PTY=%d ('%s') PS='%s' AF1=%3.2f (%d) AF2=%3.2f (%d) PI=%d\n", variant, rds_info.PTY, PTY_text[rds_info.PTY], rds_info.PS, AF1, AFc1, AF2, AFc2, PI);
540 case GROUP_14B: /* Enhanced Other Networks information */
541 if (OutputFlags & RDS_OUTPUT_EON)
542 printf("GRP14B: Enhanced Other Networks information\n");
545 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
548 case GROUP_15B: /* Fast basic tuning and switching information */
549 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
550 printf("GRP15B: Fast basic tuning and switching information\n");
553 if (OutputFlags & RDS_OUTPUT_UNKNGRP)
554 printf("unkn. RDS group %d\n", grp_type);
558 /* done with this group */