1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (c) 2011 by Bertrik Sikken
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
30 // timeout before segment obsolescence
31 #define PS_SEGMENT_TIMEOUT (HZ / 2)
32 #define RT_SEGMENT_TIMEOUT (10 * HZ)
34 /* programme identification */
35 static uint16_t pi_code
;
36 static uint16_t pi_last
;
37 /* program service name */
38 static char ps_data
[9];
39 static char ps_copy
[9];
40 static long ps_segment_timeout
[4];
41 static int ps_segment
;// bitmap of received segments
43 static char rt_data
[65];
44 static char rt_copy
[65];
45 static long rt_segment_timeout
[16];
46 static int rt_segment
;// bitmap of received segments
49 static time_t ct_data
;
51 #ifdef RDS_ISR_PROCESSING
52 /* Functions are called in ISR context */
53 #define rds_disable_irq_save() disable_irq_save()
54 #define rds_restore_irq(old) restore_irq(old)
55 /* Need triple buffer so string isn't clobbered while caller is using it */
56 static inline char * get_ps(void)
58 static char ps_out
[9];
59 int oldlevel
= rds_disable_irq_save();
60 strcpy(ps_out
, ps_copy
);
61 rds_restore_irq(oldlevel
);
64 static inline char * get_rt(void)
66 static char rt_out
[65];
67 int oldlevel
= rds_disable_irq_save();
68 strcpy(rt_out
, rt_copy
);
69 rds_restore_irq(oldlevel
);
72 #else /* ndef RDS_ISR_PROCESSING */
73 #define rds_disable_irq_save() 0
74 #define rds_restore_irq(old) ((void)(old))
75 static inline char * get_ps(void) { return ps_copy
; }
76 static inline char * get_rt(void) { return rt_copy
; }
77 #endif /* RDS_ISR_PROCESSING */
79 /* resets the rds parser */
82 int oldlevel
= rds_disable_irq_save();
92 rds_restore_irq(oldlevel
);
95 /* initialises the rds parser */
101 /* handles a group 0 packet, returns true if a new message was received */
102 static bool handle_group0(uint16_t data
[4])
106 /* remove obsolete segments */
107 for(int i
= 0; i
< 4; i
++)
108 if(TIME_AFTER(current_tick
, ps_segment_timeout
[i
]))
109 ps_segment
&= ~(1 << i
);
111 segment
= data
[1] & 3;
115 ps_data
[pos
++] = (data
[3] >> 8) & 0xFF;
116 ps_data
[pos
++] = (data
[3] >> 0) & 0xFF;
117 ps_segment
|= 1 << segment
;
118 ps_segment_timeout
[segment
] = current_tick
+ PS_SEGMENT_TIMEOUT
;
119 if (ps_segment
== 0xf) {
121 if (strcmp(ps_copy
, ps_data
) != 0) {
122 /* we got an updated message */
123 strcpy(ps_copy
, ps_data
);
130 /* handles a radio text characters, returns true if end-of-line found */
131 static bool handle_rt(int pos
, char c
)
135 /* line break hint */
148 /* handles a group 2 packet, returns true if a new message was received */
149 static bool handle_group2(uint16_t data
[4])
151 int abflag
, segment
, version
, pos
;
154 /* remove obsolete segments */
155 for(int i
= 0; i
< 16; i
++)
156 if(TIME_AFTER(current_tick
, rt_segment_timeout
[i
]))
157 rt_segment
&= ~(1 << i
);
159 /* reset parsing if the message type changed */
160 abflag
= (data
[1] >> 4) & 1;
161 segment
= data
[1] & 0xF;
162 if (abflag
!= rt_abflag
) {
167 rt_segment
|= 1 << segment
;
168 rt_segment_timeout
[segment
] = current_tick
+ RT_SEGMENT_TIMEOUT
;
171 version
= (data
[1] >> 11) & 1;
174 done
= done
|| handle_rt(pos
++, (data
[2] >> 8) & 0xFF);
175 done
= done
|| handle_rt(pos
++, (data
[2] >> 0) & 0xFF);
176 done
= done
|| handle_rt(pos
++, (data
[3] >> 8) & 0xFF);
177 done
= done
|| handle_rt(pos
++, (data
[3] >> 0) & 0xFF);
180 done
= done
|| handle_rt(pos
++, (data
[3] >> 8) & 0xFF);
181 done
= done
|| handle_rt(pos
++, (data
[3] >> 0) & 0xFF);
183 /* there are two cases for completion:
184 * - we got all 16 segments
185 * - we found a end of line AND we got all segments before it */
186 if (rt_segment
== 0xffff || (done
&& rt_segment
== (1 << segment
) - 1)) {
188 if (strcmp(rt_copy
, rt_data
) != 0) {
189 /* we got an updated message */
190 strcpy(rt_copy
, rt_data
);
198 /* handles a group 4a packet (clock-time) */
199 static bool handle_group4a(uint16_t data
[4])
201 int daycode
= ((data
[1] << 15) & 0x18000) |
202 ((data
[2] >> 1) & 0x07FFF);
203 int hour
= ((data
[2] << 4) & 0x10) |
204 ((data
[3] >> 12) & 0x0F);
205 int minute
= ((data
[3] >> 6) & 0x3F);
206 int offset_sig
= (data
[3] >> 5) & 1;
207 int offset_abs
= data
[3] & 0x1F;
209 if (daycode
< 55927) {
210 /* invalid date, before 2012-01-01 */
213 if ((hour
>= 24) || (minute
>= 60)) {
217 if (offset_abs
> 24) {
218 /* invalid local time offset */
222 /* convert modified julian day + time to UTC */
223 time_t seconds
= (daycode
- 40587) * 86400;
224 seconds
+= hour
* 3600;
225 seconds
+= minute
* 60;
226 seconds
+= ((offset_sig
== 0) ? offset_abs
: -offset_abs
) * 1800;
232 /* processes one rds packet, returns true if a new message was received */
233 bool rds_process(uint16_t data
[4])
237 /* process programme identification (PI) code */
238 uint16_t pi
= data
[0];
244 /* handle rds data based on group */
245 group
= (data
[1] >> 11) & 0x1F;
248 case 0: /* group 0A: basic info */
249 case 1: /* group 0B: basic info */
250 return handle_group0(data
);
252 case 4: /* group 2A: radio text */
253 case 5: /* group 2B: radio text */
254 return handle_group2(data
);
256 case 8: /* group 4A: clock-time */
257 return handle_group4a(data
);
266 /* TODO: The caller really should provide the buffer in order to regulate
269 /* returns the programme identification code */
270 uint16_t rds_get_pi(void)
275 /* returns the most recent valid programme service name */
276 char* rds_get_ps(void)
281 /* returns the most recent valid RadioText message */
282 char* rds_get_rt(void)
287 /* returns the most recent valid clock-time value (or 0 if invalid) */
288 time_t rds_get_ct(void)