diff options
Diffstat (limited to 'firmware/drivers/rds.c')
-rw-r--r-- | firmware/drivers/rds.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/firmware/drivers/rds.c b/firmware/drivers/rds.c new file mode 100644 index 0000000000..09bc33807c --- /dev/null +++ b/firmware/drivers/rds.c @@ -0,0 +1,192 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2011 by Bertrik Sikken + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <strlcpy.h> +#include "rds.h" + +/* programme identification */ +static uint16_t pi; +/* program service name */ +static char ps_data[9]; +static char ps_copy[9]; +static int ps_segment; +/* radio text */ +static char rt_data[65]; +static char rt_copy[65]; +static int rt_segment; +static int rt_abflag; + +/* resets the rds parser */ +void rds_reset(void) +{ + ps_copy[0] = '\0'; + ps_segment = 0; + rt_copy[0] = '\0'; + rt_segment = 0; + pi = 0; +} + +/* initialises the rds parser */ +void rds_init(void) +{ + rds_reset(); +} + +/* handles a group 0 packet, returns true if a new message was received */ +static bool handle_group0(uint16_t data[4]) +{ + int segment, pos; + + segment = data[1] & 3; + + /* reset parsing if not in expected order */ + if (segment != ps_segment) { + ps_segment = 0; + if (segment != 0) { + return false; + } + } + + /* store data */ + pos = segment * 2; + ps_data[pos++] = (data[3] >> 8) & 0xFF; + ps_data[pos++] = (data[3] >> 0) & 0xFF; + if (++ps_segment == 4) { + ps_data[pos] = '\0'; + if (strcmp(ps_copy, ps_data) != 0) { + /* we got an updated message */ + strcpy(ps_copy, ps_data); + return true; + } + } + return false; +} + +/* handles a radio text characters, returns true if end-of-line found */ +static bool handle_rt(int pos, char c) +{ + switch (c) { + case 0x0A: + /* line break hint */ + rt_data[pos] = ' '; + return false; + case 0x0D: + /* end of line */ + rt_data[pos] = '\0'; + return true; + default: + rt_data[pos] = c; + return false; + } +} + +/* handles a group 2 packet, returns true if a new message was received */ +static bool handle_group2(uint16_t data[4]) +{ + int abflag, segment, version, pos; + bool done; + + /* reset parsing if not in expected order */ + abflag = (data[1] >> 4) & 1; + segment = data[1] & 0xF; + if ((abflag != rt_abflag) || (segment != rt_segment)) { + rt_abflag = abflag; + rt_segment = 0; + if (segment != 0) { + return false; + } + } + + /* store data */ + version = (data[1] >> 11) & 1; + done = false; + if (version == 0) { + pos = segment * 4; + done = done || handle_rt(pos++, (data[2] >> 8) & 0xFF); + done = done || handle_rt(pos++, (data[2] >> 0) & 0xFF); + done = done || handle_rt(pos++, (data[3] >> 8) & 0xFF); + done = done || handle_rt(pos++, (data[3] >> 0) & 0xFF); + } else { + pos = segment * 2; + done = done || handle_rt(pos++, (data[3] >> 8) & 0xFF); + done = done || handle_rt(pos++, (data[3] >> 0) & 0xFF); + } + if ((++rt_segment == 16) || done) { + rt_data[pos] = '\0'; + if (strcmp(rt_copy, rt_data) != 0) { + /* we got an updated message */ + strcpy(rt_copy, rt_data); + return true; + } + } + + return false; +} + +/* processes one rds packet, returns true if a new message was received */ +bool rds_process(uint16_t data[4]) +{ + int group; + + /* get programme identification */ + if (pi == 0) { + pi = data[0]; + } + + /* handle rds data based on group */ + group = (data[1] >> 11) & 0x1F; + switch (group) { + + case 0: /* group 0A: basic info */ + case 1: /* group 0B: basic info */ + return handle_group0(data); + + case 4: /* group 2A: radio text */ + case 5: /* group 2B: radio text */ + return handle_group2(data); + + default: + break; + } + + return false; +} + +/* returns the programme identification code */ +uint16_t rds_get_pi(void) +{ + return pi; +} + +/* returns the most recent valid programme service name */ +char* rds_get_ps(void) +{ + return ps_copy; +} + +/* returns the most recent valid RadioText message */ +char* rds_get_rt(void) +{ + return rt_copy; +} + |