initial commit to new repo
This commit is contained in:
parent
09bc9a5042
commit
42e3bad75a
54 changed files with 2924 additions and 1 deletions
46
decoder/Makefile
Normal file
46
decoder/Makefile
Normal file
|
@ -0,0 +1,46 @@
|
|||
CC ?= gcc
|
||||
|
||||
# prefix for installation and search path (like icons)
|
||||
PREFIX = /usr/local/
|
||||
|
||||
# for normal desktop GTK+
|
||||
CCFLAGS = -Wall -O2 -g
|
||||
|
||||
SQLITECFLAGS = `pkg-config --cflags sqlite3`
|
||||
GTKCFLAGS = `pkg-config --cflags gtk+-2.0`
|
||||
|
||||
CFLAGS = $(CCFLAGS) $(SQLITECFLAGS) $(GTKCFLAGS)
|
||||
|
||||
SQLITELDFLAGS = `pkg-config --libs sqlite3`
|
||||
GTKLDFLAGS = `pkg-config --libs gtk+-2.0`
|
||||
|
||||
# no need to change anything below this line
|
||||
# ------------------------------------------
|
||||
|
||||
.SUFFIXES: .d .c
|
||||
|
||||
CFLAGS += -MD -DPREFIX=\"$(PREFIX)\" $(OPTIONS)
|
||||
LDFLAGS = $(CLDFLAGS) $(SQLITELDFLAGS)
|
||||
|
||||
RDS_MEMBERS = rds bitstream tmc rds_test
|
||||
SOURCES = $(patsubst %,%.c,$(RDS_MEMBERS))
|
||||
OBJS = $(patsubst %,%.o,$(RDS_MEMBERS))
|
||||
DEPS = $(patsubst %,%.d,$(RDS_MEMBERS))
|
||||
|
||||
UR_MEMBERS = rds bitstream tmc uberradio
|
||||
UR_SOURCES = $(patsubst %,%.c,$(UR_MEMBERS))
|
||||
UR_OBJS = $(patsubst %,%.o,$(UR_MEMBERS))
|
||||
UR_DEPS = $(patsubst %,%.d,$(UR_MEMBERS))
|
||||
|
||||
all: rds_test
|
||||
|
||||
rds_test: $(OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
uberradio: $(UR_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(GTKLDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.d rdsread rds_test uberradio
|
||||
|
||||
-include $(DEPS)
|
4
decoder/Readme.txt
Normal file
4
decoder/Readme.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
All files contained in here are licensed under the GPL where no other
|
||||
license applies.
|
||||
(I will clean this up a little later)
|
82
decoder/bitstream.c
Normal file
82
decoder/bitstream.c
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bitstream.h"
|
||||
|
||||
static unsigned char *_BITBUF;
|
||||
static unsigned int _BITPOS;
|
||||
static unsigned int _BUFLEN;
|
||||
|
||||
void printbits(unsigned int val)
|
||||
{
|
||||
unsigned char i=0;
|
||||
|
||||
while (i<32)
|
||||
if (val & (1<<(31-i++)))
|
||||
printf("1 ");
|
||||
else
|
||||
printf("0 ");
|
||||
// printf("\n");
|
||||
}
|
||||
|
||||
void printbuf_head(unsigned char *buf)
|
||||
{
|
||||
//printbits(buf[3]<<24 | buf[2]<<16 | buf[1]<<8 | buf[0]);
|
||||
printbits(buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]);
|
||||
}
|
||||
|
||||
unsigned int bitstream_get_bits(unsigned char *data, unsigned int bitOffset, unsigned int numBits)
|
||||
{
|
||||
unsigned int val;
|
||||
unsigned int mask = (1 << numBits) - 1;
|
||||
|
||||
data += ((bitOffset / 8) -3);
|
||||
val = *((int *)data);
|
||||
// printf("0x%08x ", val);
|
||||
|
||||
val = val << (bitOffset % 8);
|
||||
// printf("0x%08x ", val);
|
||||
|
||||
val = val >> (32-numBits);
|
||||
// printf("0x%08x %d ", val, (32-numBits));
|
||||
|
||||
val &= mask;
|
||||
// printf("0x%08x\n", val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int bitstream_bits_remaining(void)
|
||||
{
|
||||
return (_BUFLEN - _BITPOS);
|
||||
}
|
||||
|
||||
unsigned int bitstream_get_next_bits(unsigned int nbits)
|
||||
{
|
||||
unsigned int res;
|
||||
|
||||
res = bitstream_get_bits(_BITBUF, _BITPOS, nbits);
|
||||
_BITPOS += nbits;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void bitstream_start(unsigned char *buf, unsigned int len)
|
||||
{
|
||||
_BITBUF = buf;
|
||||
_BITPOS = 0;
|
||||
_BUFLEN = len;
|
||||
}
|
||||
|
||||
|
23
decoder/bitstream.h
Normal file
23
decoder/bitstream.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _BITSTREAM_H
|
||||
#define _BITSTREAM_H
|
||||
|
||||
unsigned int bitstream_get_bits(unsigned char *data, unsigned int bitOffset, unsigned int numBits);
|
||||
|
||||
unsigned int bitstream_get_next_bits(unsigned int nbits);
|
||||
|
||||
int bitstream_bits_remaining(void);
|
||||
|
||||
void bitstream_start(unsigned char *buf, unsigned int len);
|
||||
|
||||
#endif
|
||||
|
BIN
decoder/lcl.db.bz2
Normal file
BIN
decoder/lcl.db.bz2
Normal file
Binary file not shown.
561
decoder/rds.c
Normal file
561
decoder/rds.c
Normal file
|
@ -0,0 +1,561 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "rds.h"
|
||||
#include "rds_consts.h"
|
||||
#include "tmc.h"
|
||||
|
||||
//#define DEBUG 1
|
||||
|
||||
|
||||
static struct rds_info_s rds_info;
|
||||
static struct tm rds_time;
|
||||
|
||||
static struct _RDS_Private {
|
||||
void (*rds_PI_cb)(unsigned short PI, unsigned char ccode, unsigned char ptype, unsigned char pref, void *user_data);
|
||||
void *rds_PI_cb_data;
|
||||
void (*rds_sname_cb)(char *sname, void *user_data);
|
||||
void *rds_sname_cb_data;
|
||||
void (*rds_sinfo_cb)(unsigned char TP, unsigned char TA, unsigned char MS, unsigned char PTY, void *user_data);
|
||||
void *rds_sinfo_cb_data;
|
||||
void (*rds_radiotext_cb)(char *radio_text, void *user_data);
|
||||
void *rds_radiotext_cb_data;
|
||||
void (*rds_date_time_cb)(struct tm *dtime, void *user_data);
|
||||
void *rds_date_time_cb_data;
|
||||
void (*rds_afinfo_cb)(unsigned char cnt, double *AF, void *user_data);
|
||||
void *rds_afinfo_cb_data;
|
||||
} _rds_private;
|
||||
|
||||
void rds_init(void) {
|
||||
memset(&_rds_private, 0, sizeof(struct _RDS_Private));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_rds_private.rds_PI_cb = rds_PI_cb;
|
||||
_rds_private.rds_PI_cb_data = user_data;
|
||||
}
|
||||
|
||||
void rds_set_sname_cb(void (*rds_sname_cb)(char *sname, void *user_data), void *user_data)
|
||||
{
|
||||
_rds_private.rds_sname_cb = rds_sname_cb;
|
||||
_rds_private.rds_sname_cb_data = user_data;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_rds_private.rds_sinfo_cb = rds_sinfo_cb;
|
||||
_rds_private.rds_sinfo_cb_data = user_data;
|
||||
}
|
||||
|
||||
void rds_set_afinfo_cb(void (*rds_afinfo_cb)(unsigned char cnt, double *AF, void *user_data), void *user_data)
|
||||
{
|
||||
_rds_private.rds_afinfo_cb = rds_afinfo_cb;
|
||||
_rds_private.rds_afinfo_cb_data = user_data;
|
||||
}
|
||||
|
||||
void rds_set_radiotext_cb(void (*rds_radiotext_cb)(char *radio_text, void *user_data), void *user_data)
|
||||
{
|
||||
_rds_private.rds_radiotext_cb = rds_radiotext_cb;
|
||||
_rds_private.rds_radiotext_cb_data = user_data;
|
||||
}
|
||||
|
||||
void rds_set_date_time_cb(void (*rds_date_time_cb)(struct tm *dtime, void *user_data), void *user_data)
|
||||
{
|
||||
_rds_private.rds_date_time_cb = rds_date_time_cb;
|
||||
_rds_private.rds_date_time_cb_data = user_data;
|
||||
}
|
||||
|
||||
|
||||
int rds_receive_group(int rds_fd, unsigned short *rdsgroup)
|
||||
{
|
||||
static unsigned char expected = 0;
|
||||
int rb;
|
||||
unsigned char rbuf[3];
|
||||
unsigned short block;
|
||||
unsigned char offset;
|
||||
|
||||
rb = read(rds_fd, rbuf, 3);
|
||||
if (rb <= 0)
|
||||
return 0;
|
||||
if (rb != 3) {
|
||||
printf("#read err rb=%d\n", rb);
|
||||
return 0;
|
||||
}
|
||||
block = rbuf[0] | (rbuf[1] << 8);
|
||||
offset = rbuf[2] & 0x07;
|
||||
|
||||
if (rbuf[2] & 0x80) {
|
||||
if (OutputFlags & RDS_RECEIVE_INDICATOR)
|
||||
printf("E");
|
||||
} else {
|
||||
// printf("group block=0x%04x offs=0x%02x\n", block, offset);
|
||||
if (offset != expected) {
|
||||
if (OutputFlags & RDS_RECEIVE_INDICATOR)
|
||||
printf("block out of sync - resetting\n");
|
||||
expected = 0;
|
||||
memset(rdsgroup, 0, sizeof(rdsgroup));
|
||||
} else {
|
||||
rdsgroup[offset] = block;
|
||||
expected++;
|
||||
if (expected >= 4) {
|
||||
expected = 0;
|
||||
if (OutputFlags & RDS_RECEIVE_INDICATOR)
|
||||
printf(".\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (OutputFlags & RDS_RECEIVE_INDICATOR)
|
||||
printf(".");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
enum RDSGroupType { GROUP_0A=0, GROUP_0B, GROUP_1A, GROUP_1B, GROUP_2A, GROUP_2B,
|
||||
GROUP_3A, GROUP_3B, GROUP_4A, GROUP_4B, GROUP_5A, GROUP_5B,
|
||||
GROUP_6A, GROUP_6B, GROUP_7A, GROUP_7B, GROUP_8A, GROUP_8B,
|
||||
GROUP_9A, GROUP_9B, GROUP_10A, GROUP_10B, GROUP_11A, GROUP_11B,
|
||||
GROUP_12A, GROUP_12B, GROUP_13A, GROUP_13B, GROUP_14A, GROUP_14B,
|
||||
GROUP_15A, GROUP_15B, GROUP_UNKNOWN };
|
||||
|
||||
void rds_radio_retuned(void)
|
||||
{
|
||||
memset(&rds_info, 0, sizeof(rds_info));
|
||||
rds_info.LTN = -1;
|
||||
memset(&rds_time, 0, sizeof(rds_time));
|
||||
}
|
||||
|
||||
void rds_decode_group(unsigned short *rdsgroup)
|
||||
{
|
||||
static unsigned short ogrp[4];
|
||||
static unsigned char grp_decoded = 0;
|
||||
static unsigned char sname_rcvd = 0;
|
||||
unsigned char grp_type = (rdsgroup[1] >> 11);
|
||||
unsigned char offs;
|
||||
static unsigned char otextAB = 0, newtext = 0;
|
||||
unsigned short PI = rdsgroup[0];
|
||||
unsigned char textAB = 0;
|
||||
int local_time_off = 0;
|
||||
int day_code = 0;
|
||||
int year_, mon_, K;
|
||||
|
||||
/* we want to wait for at least two identical group transmits */
|
||||
if (ogrp[0] != rdsgroup[0] || ogrp[1] != rdsgroup[1] || ogrp[2] != rdsgroup[2] || ogrp[3] != rdsgroup[3]) {
|
||||
ogrp[0] = rdsgroup[0];
|
||||
ogrp[1] = rdsgroup[1];
|
||||
ogrp[2] = rdsgroup[2];
|
||||
ogrp[3] = rdsgroup[3];
|
||||
//printf("grp: 0x%04x 0x%04x 0x%04x 0x%04x\n", rdsgroup[0], rdsgroup[1], rdsgroup[2], rdsgroup[3]);
|
||||
grp_decoded = 0;
|
||||
return;
|
||||
}
|
||||
if (grp_decoded)
|
||||
return;
|
||||
|
||||
if (rds_info.PI != PI) {
|
||||
/* station change? */
|
||||
memset(&rds_info, 0, sizeof(rds_info));
|
||||
rds_info.LTN = -1;
|
||||
memset(&rds_time, 0, sizeof(rds_time));
|
||||
rds_info.ccode = (PI & 0xf000) >> 12;
|
||||
rds_info.ptype = (PI & 0x0f00) >> 8;
|
||||
rds_info.pref = (PI & 0x00ff);
|
||||
sname_rcvd = 0;
|
||||
if (rds_info.pref == 0) /* something is wrong here */
|
||||
return;
|
||||
rds_info.PI = rdsgroup[0];
|
||||
if (_rds_private.rds_PI_cb != NULL)
|
||||
_rds_private.rds_PI_cb(rds_info.PI, rds_info.ccode, rds_info.ptype, rds_info.pref, _rds_private.rds_PI_cb_data);
|
||||
if (OutputFlags & RDS_OUTPUT_RDSINFO)
|
||||
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);
|
||||
}
|
||||
|
||||
switch (grp_type) {
|
||||
case GROUP_0A: { /* basic switching and tuning */
|
||||
unsigned char AFc1, AFc2;
|
||||
float AF1=0, AF2=0;
|
||||
|
||||
offs = (rdsgroup[1] & 0x03);
|
||||
if (offs == 0)
|
||||
sname_rcvd = 0;
|
||||
if (offs == 1 && sname_rcvd == 0)
|
||||
sname_rcvd = 1;
|
||||
if (offs == 2 && sname_rcvd == 1)
|
||||
sname_rcvd = 2;
|
||||
if (offs == 3 && sname_rcvd == 2)
|
||||
sname_rcvd = 3;
|
||||
rds_info.sname[offs*2] = ((rdsgroup[3] & 0xff00) >> 8);
|
||||
rds_info.sname[(offs*2)+1] = rdsgroup[3] & 0x00ff;
|
||||
if (_rds_private.rds_sname_cb != NULL && sname_rcvd == 3) {
|
||||
_rds_private.rds_sname_cb(rds_info.sname, _rds_private.rds_sname_cb_data);
|
||||
sname_rcvd = 0;
|
||||
}
|
||||
|
||||
rds_info.TA = (rdsgroup[1] & 0x10) >> 4;
|
||||
rds_info.TP = (rdsgroup[1] & 0x400) >> 10;
|
||||
rds_info.MS = (rdsgroup[1] & 0x08) >> 3;
|
||||
rds_info.PTY = (rdsgroup[1] & 0x3e0) >> 5;
|
||||
if (_rds_private.rds_sinfo_cb != NULL)
|
||||
_rds_private.rds_sinfo_cb(rds_info.TP, rds_info.TA, rds_info.MS, rds_info.PTY, _rds_private.rds_sinfo_cb_data);
|
||||
|
||||
AFc1 = ((rdsgroup[2] & 0xff00) >> 8);
|
||||
AFc2 = (rdsgroup[2] & 0x00ff);
|
||||
if (AFc1 > 225 && AFc1 < 250) {
|
||||
if (OutputFlags & RDS_OUTPUT_STATION_ID)
|
||||
printf(" %d AFs follow:\n", AFc1-224);
|
||||
} else if (AFc1 > 0 && AFc1 < 205)
|
||||
AF1 = (float)87.5 + ((float)AFc1 * .1);
|
||||
else if (AFc1 == 205)
|
||||
printf(" filler- \n");
|
||||
if (AFc2 > 0 && AFc2 < 205)
|
||||
AF2 = (float)87.5 + ((float)AFc2 * .1);
|
||||
|
||||
if (OutputFlags & RDS_OUTPUT_STATION_ID)
|
||||
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]);
|
||||
}
|
||||
break;
|
||||
case GROUP_0B:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("group 0B\n");
|
||||
break;
|
||||
case GROUP_1A: { /* Programme Item Number and slow labelling codes */
|
||||
unsigned char variant, day, hour, minute;
|
||||
unsigned short tmc_id;
|
||||
|
||||
variant = (rdsgroup[2] & (0x07 << 12)) >> 12;
|
||||
day = (rdsgroup[3] & (0x1f << 11)) >> 11;
|
||||
hour = (rdsgroup[3] & (0x1f << 6)) >> 6;
|
||||
minute = (rdsgroup[3] & 0x3f);
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("1A var=%d day=%d %d:%d ", variant, day, hour, minute);
|
||||
if (variant == 0) {
|
||||
rds_info.ECC = (rdsgroup[2] & 0xff);
|
||||
printf(" ccode=%d ECC=0x%02x '%s' ", rds_info.ccode, rds_info.ECC, ECC_text[((rds_info.ECC & 0x0f)*16)+(rds_info.ccode-1)]);
|
||||
}
|
||||
if (variant == 1) {
|
||||
tmc_id = (rdsgroup[2] & 0x0fff);
|
||||
printf(" TMC ID=0x%04x", tmc_id);
|
||||
}
|
||||
if (variant == 2) {
|
||||
printf(" Paging ID=0x%04x", (rdsgroup[2] & 0x0fff));
|
||||
}
|
||||
if (variant == 3) {
|
||||
rds_info.lang_code = (rdsgroup[2] & 0xff);
|
||||
printf(" language=0x%02x ", rds_info.lang_code);
|
||||
switch (rds_info.lang_code) {
|
||||
case 0x03:
|
||||
printf("Catalan ");
|
||||
break;
|
||||
case 0x08:
|
||||
printf("German ");
|
||||
break;
|
||||
case 0x0a:
|
||||
printf("Spanish ");
|
||||
break;
|
||||
case 0x15:
|
||||
printf("Italian ");
|
||||
break;
|
||||
case 0x18:
|
||||
printf("Latvian ");
|
||||
break;
|
||||
case 0x1d:
|
||||
printf("Dutch ");
|
||||
break;
|
||||
case 0x27:
|
||||
printf("Finnish ");
|
||||
break;
|
||||
default:
|
||||
printf("unknown ");
|
||||
break;
|
||||
};
|
||||
}
|
||||
if (variant == 7) {
|
||||
printf(" EWS Channel ID=0x%04x", (rdsgroup[2] & 0x0fff));
|
||||
}
|
||||
printf("\n");
|
||||
} break;
|
||||
case GROUP_1B:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("group 1B\n");
|
||||
break;
|
||||
case GROUP_2A: /* 2A has 64 chars, 4 per group */
|
||||
case GROUP_2B: /* 2B has 32 chars, 2 per group */
|
||||
offs = (rdsgroup[1] & 0x0f);
|
||||
textAB = (rdsgroup[1] & 0x10);
|
||||
if (textAB != otextAB) {
|
||||
memset(rds_info.rtext, ' ', 64);
|
||||
rds_info.rtext[64] = '\0';
|
||||
}
|
||||
otextAB = textAB;
|
||||
if (grp_type == GROUP_2A) {
|
||||
rds_info.rtext[offs*4] = ((rdsgroup[2] & 0xff00) >> 8);
|
||||
rds_info.rtext[(offs*4)+1] = (rdsgroup[2] & 0x00ff);
|
||||
rds_info.rtext[(offs*4)+2] = ((rdsgroup[3] & 0xff00) >> 8);
|
||||
rds_info.rtext[(offs*4)+3] = (rdsgroup[3] & 0x00ff);
|
||||
} else {
|
||||
rds_info.rtext[offs*2] = ((rdsgroup[3] & 0xff00) >> 8);
|
||||
rds_info.rtext[(offs*2)+1] = (rdsgroup[3] & 0x00ff);
|
||||
}
|
||||
if (offs == 0) /* new text always starts at 0 */
|
||||
newtext = 1;
|
||||
if ((offs == 15)) { /* all parts received */
|
||||
if (newtext == 1) {
|
||||
if (_rds_private.rds_radiotext_cb != NULL)
|
||||
_rds_private.rds_radiotext_cb(rds_info.rtext, _rds_private.rds_radiotext_cb_data);
|
||||
if (OutputFlags & RDS_OUTPUT_RADIO_TEXT)
|
||||
printf("RT '%s'\n", rds_info.rtext);
|
||||
newtext = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GROUP_3A: /* ODA - TMC alarm, */
|
||||
rds_info.AID = rdsgroup[3];
|
||||
/* TMC AID */
|
||||
if (rds_info.AID == 0xcd46 || rds_info.AID == 0x0d45) {
|
||||
if ((rdsgroup[2] & 0xc000) == 0) {
|
||||
rds_info.LTN = (rdsgroup[2] & 0x0fc0) >> 6;
|
||||
rds_info.AFI = (rdsgroup[2] & 0x0020) >> 5;
|
||||
rds_info.M = (rdsgroup[2] & 0x0010) >> 4;
|
||||
rds_info.I = (rdsgroup[2] & 0x0008) >> 3;
|
||||
rds_info.N = (rdsgroup[2] & 0x0004) >> 2;
|
||||
rds_info.R = (rdsgroup[2] & 0x0002) >> 1;
|
||||
rds_info.U = (rdsgroup[2] & 0x0001);
|
||||
}
|
||||
if (rdsgroup[2] & 0x4000) {
|
||||
/* SID */
|
||||
if (rds_info.M) {
|
||||
rds_info.G = (rdsgroup[2] & 0x3000) >> 12;
|
||||
rds_info.Ta = (rdsgroup[2] & 0x0030) >> 4;
|
||||
rds_info.Tw = (rdsgroup[2] & 0x000c) >> 2;
|
||||
rds_info.Td = (rdsgroup[2] & 0x0003);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
if (OutputFlags & RDS_OUTPUT_RDSINFO) {
|
||||
printf("AID = 0x%04x\n", rds_info.AID);
|
||||
printf("LTN ID = %d (0x%02x)\n", rds_info.LTN, rds_info.LTN);
|
||||
printf("Traffic information: ");
|
||||
if (rds_info.I)
|
||||
printf("international ");
|
||||
if (rds_info.N)
|
||||
printf("national ");
|
||||
if (rds_info.R)
|
||||
printf("regional ");
|
||||
if (rds_info.U)
|
||||
printf("urban ");
|
||||
printf("\n");
|
||||
}
|
||||
} else if (rds_info.AID == 0x4bd7) {
|
||||
if (OutputFlags & RDS_OUTPUT_RDSINFO) {
|
||||
printf("RT+\nG2 = 0x%04x\n", rdsgroup[2]);
|
||||
printf("template = 0x%02x\n", (rdsgroup[2] & 0x00ff));
|
||||
printf("SCB = 0x%02x\n", (rdsgroup[2] & 0x0f00)>>8);
|
||||
printf("CB = 0x%02x\n", (rdsgroup[2] & 0x1000)>>12);
|
||||
printf("rfu = 0x%02x\n", (rdsgroup[2] & 0xe000)>>13);
|
||||
}
|
||||
} else {
|
||||
printf("3A AID=0x%04x\n", rds_info.AID);
|
||||
}
|
||||
break;
|
||||
case GROUP_3B:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("group 3B\n");
|
||||
break;
|
||||
case GROUP_4A:
|
||||
//printf("group 4A - time/date\n");
|
||||
rds_time.tm_sec = 0; // date/time message is sent at 00 secs +/- .2sec
|
||||
rds_time.tm_min = ((rdsgroup[3] & (0x3f << 6)) >> 6);
|
||||
rds_time.tm_hour = ((rdsgroup[3] & (0x0f << 12)) >> 12) | ((rdsgroup[2] & 0x01) << 4);
|
||||
local_time_off = (rdsgroup[3] & 0x0f) * ((rdsgroup[3] & 0x10) ? -1 : 1);
|
||||
rds_time.tm_hour += local_time_off / 2;
|
||||
rds_time.tm_min = (rds_time.tm_min + ((local_time_off % 2) * 30)) % 60;
|
||||
|
||||
day_code = (((rdsgroup[2] & 0xfffe) >> 1) | ((rdsgroup[1] & 0x03) << 15)) /*+ local_time_off*/;
|
||||
|
||||
year_ = ((float)day_code - 15078.2) / 365.25;
|
||||
mon_ = ((day_code - 14956.1) - (int)(year_ * 365.25)) / 30.6001;
|
||||
rds_time.tm_mday = day_code - 14956 - (int)(year_ * 365.25) - (int)(mon_ * 30.6001);
|
||||
if (mon_ == 14 || mon_ == 15)
|
||||
K = 1;
|
||||
else
|
||||
K = 0;
|
||||
rds_time.tm_year = year_ + K;
|
||||
rds_time.tm_mon = mon_ - 1 - (K * 12) - 1;
|
||||
|
||||
rds_time.tm_isdst = -1;
|
||||
|
||||
if (_rds_private.rds_date_time_cb != NULL)
|
||||
_rds_private.rds_date_time_cb(&rds_time, _rds_private.rds_date_time_cb_data);
|
||||
|
||||
if (OutputFlags & RDS_OUTPUT_DATETIME)
|
||||
printf("%s", asctime(&rds_time));
|
||||
#ifdef DEBUG
|
||||
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);
|
||||
#endif
|
||||
//printf("day=%d month=%d year=%d\n", day, mon, year);
|
||||
break;
|
||||
case GROUP_4B:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP4B\n");
|
||||
break;
|
||||
case GROUP_5A:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP5A\n");
|
||||
break;
|
||||
case GROUP_5B:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP5B\n");
|
||||
break;
|
||||
case GROUP_6A:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP6A\n");
|
||||
break;
|
||||
case GROUP_6B:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP6B\n");
|
||||
break;
|
||||
case GROUP_7A:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP7A\n");
|
||||
break;
|
||||
case GROUP_7B:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP7B\n");
|
||||
break;
|
||||
case GROUP_8A:
|
||||
if (rds_info.LTN != 0) // non TMCpro
|
||||
decode_tmc(rdsgroup);
|
||||
if (rds_info.LTN == 0)
|
||||
printf("TMCpro\n");
|
||||
break;
|
||||
case GROUP_8B:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP8B\n");
|
||||
break;
|
||||
case GROUP_9A:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP9A\n");
|
||||
break;
|
||||
case GROUP_9B:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP9B\n");
|
||||
break;
|
||||
case GROUP_10A:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP10A\n");
|
||||
break;
|
||||
case GROUP_10B:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP10B\n");
|
||||
break;
|
||||
case GROUP_11A: /* Open Data Application ODA */
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP11A ODA: #1=0x%02x #2=0x%04x #3=0x%04x\n", rdsgroup[1] & 0x1f, rdsgroup[2], rdsgroup[3]);
|
||||
|
||||
/* we previously got an RT+ identifier, try RT+ decoding */
|
||||
if (rds_info.AID == 0x4bd7) {
|
||||
printf("RT+\ntoggle: %s\n", (rdsgroup[1] & 0x10) ? "yes" : "no");
|
||||
printf("item running : %s\n", (rdsgroup[1] & 0x08) ? "yes" : "no");
|
||||
printf("content type 1 : %d\n", (rdsgroup[1] & 0x07) << 3 | (rdsgroup[2] & 0x0e00) >> 13);
|
||||
printf("start marker 1 : %d\n", (rdsgroup[2] & 0x1f80) >> 7);
|
||||
printf("length marker 1: %d\n", (rdsgroup[2] & 0x007e) >> 1);
|
||||
printf("content type 2 : %d\n", (rdsgroup[1] & 0x01) << 4);
|
||||
printf("start marker 2 : %d\n", (rdsgroup[2] & 0x07e0) >> 5);
|
||||
printf("length marker 2: %d\n", (rdsgroup[2] & 0x001f));
|
||||
}
|
||||
break;
|
||||
case GROUP_11B: /* Open Data Application ODA */
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP11B\n");
|
||||
printf("11B ODA: #1=0x%02x PI=0x%04x #3=0x%04x\n", rdsgroup[1] & 0x1f, rdsgroup[2], rdsgroup[3]);
|
||||
break;
|
||||
case GROUP_12A: /* Open Data Application ODA */
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP12A\n");
|
||||
break;
|
||||
case GROUP_12B: /* Open Data Application ODA */
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP12B\n");
|
||||
break;
|
||||
case GROUP_13A: /* Open Data Application ODA or paging */
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP13A\n");
|
||||
break;
|
||||
case GROUP_13B: /* Open Data Application ODA */
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP13B\n");
|
||||
break;
|
||||
case GROUP_14A: /* EON */ {
|
||||
unsigned char variant = rdsgroup[1] & 0x0f;
|
||||
unsigned char AFc1=0, AFc2=0;
|
||||
float AF1=0, AF2=0;
|
||||
unsigned short PI;
|
||||
|
||||
rds_info.PTY = (rdsgroup[1] & 0x3e0) >> 5;
|
||||
rds_info.TPTN = (rdsgroup[1] & 0x400) >> 10;
|
||||
rds_info.TPON = (rdsgroup[1] & 0x10) >> 4;
|
||||
PI = rdsgroup[3];
|
||||
|
||||
if (variant <= 3) { // PS
|
||||
rds_info.PS[variant*2] = ((rdsgroup[2] & 0xff00) >> 8);
|
||||
rds_info.PS[(variant*2)+1] = (rdsgroup[2] & 0x00ff);
|
||||
} else if (variant == 4) {
|
||||
printf("EON variant 4\n");
|
||||
} else if (variant >= 5 && variant <= 8) { // AF - alternate frequency
|
||||
AFc1 = ((rdsgroup[2] & 0xff00) >> 8);
|
||||
AFc2 = (rdsgroup[2] & 0x00ff);
|
||||
if (AFc1 > 0 && AFc1 < 205)
|
||||
AF1 = (float)87.5 + ((float)AFc1 * .1);
|
||||
else if (AFc1 >= 225 && AFc1 <= 249)
|
||||
AF1 = AFc1 - 224;
|
||||
if (AFc2 > 0 && AFc2 < 205)
|
||||
AF2 = (float)87.5 + ((float)AFc2 * .1);
|
||||
else if (AFc2 >= 225 && AFc2 <= 249)
|
||||
AF2 = AFc2 - 224;
|
||||
}
|
||||
if (OutputFlags & RDS_OUTPUT_EON) {
|
||||
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);
|
||||
}
|
||||
} break;
|
||||
case GROUP_14B: /* Enhanced Other Networks information */
|
||||
if (OutputFlags & RDS_OUTPUT_EON)
|
||||
printf("GRP14B: Enhanced Other Networks information\n");
|
||||
break;
|
||||
case GROUP_15A:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP15A\n");
|
||||
break;
|
||||
case GROUP_15B: /* Fast basic tuning and switching information */
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("GRP15B: Fast basic tuning and switching information\n");
|
||||
break;
|
||||
default:
|
||||
if (OutputFlags & RDS_OUTPUT_UNKNGRP)
|
||||
printf("unkn. RDS group %d\n", grp_type);
|
||||
break;
|
||||
}
|
||||
|
||||
/* done with this group */
|
||||
grp_decoded = 1;
|
||||
}
|
||||
|
78
decoder/rds.h
Normal file
78
decoder/rds.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#define RDS_RECEIVE_INDICATOR 1 << 0
|
||||
#define RDS_OUTPUT_STATION_ID 1 << 1
|
||||
#define RDS_OUTPUT_RADIO_TEXT 1 << 2
|
||||
#define RDS_OUTPUT_TMC 1 << 3
|
||||
#define RDS_OUTPUT_DATETIME 1 << 4
|
||||
#define RDS_OUTPUT_RDSINFO 1 << 5
|
||||
#define RDS_OUTPUT_EON 1 << 6
|
||||
#define RDS_OUTPUT_UNKNGRP 1 << 7
|
||||
|
||||
extern int OutputFlags;
|
||||
|
||||
/* defined in rds_consts.h */
|
||||
extern const char *PTY_text[];
|
||||
extern const char *ECC_text[];
|
||||
extern const char *ptype_stext[];
|
||||
extern const char *ptype_ltext[];
|
||||
|
||||
struct rds_info_s {
|
||||
struct tm dtime;
|
||||
char sname[9];
|
||||
char rtext[65];
|
||||
unsigned short AID;
|
||||
char LTN; /* location table number */
|
||||
unsigned char AFI;
|
||||
unsigned char M;
|
||||
unsigned char I;
|
||||
unsigned char N;
|
||||
unsigned char R;
|
||||
unsigned char U;
|
||||
unsigned char G;
|
||||
unsigned char SID;
|
||||
unsigned char Ta;
|
||||
unsigned char Tw;
|
||||
unsigned char Td;
|
||||
unsigned char PTY;
|
||||
unsigned char TPTN;
|
||||
unsigned char TPON;
|
||||
unsigned char PS[9];
|
||||
unsigned short PI;
|
||||
unsigned char ccode, ptype, pref;
|
||||
unsigned char ECC;
|
||||
unsigned char lang_code;
|
||||
unsigned char MS;
|
||||
unsigned char TP;
|
||||
unsigned char TA;
|
||||
};
|
||||
|
||||
int rds_receive_group(int rds_fd, unsigned short *rdsgroup);
|
||||
void rds_radio_retuned(void);
|
||||
void rds_decode_group(unsigned short *rdsgroup);
|
||||
|
||||
void rds_init(void);
|
||||
|
||||
/* with every group but only once reported when PI changes */
|
||||
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);
|
||||
|
||||
/* group 0A */
|
||||
void rds_set_sname_cb(void (*rds_sname_cb)(char *sname, void *user_data), void *user_data);
|
||||
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);
|
||||
void rds_set_afinfo_cb(void (*rds_afinfo_cb)(unsigned char cnt, double *AF, void *user_data), void *user_data);
|
||||
/* group 2A/B */
|
||||
void rds_set_radiotext_cb(void (*rds_radiotext_cb)(char *radio_text, void *user_data), void *user_data);
|
||||
/* group 3A */
|
||||
|
||||
/* group 4A */
|
||||
void rds_set_date_time_cb(void (*rds_date_time_cb)(struct tm *rds_time, void *user_data), void *user_data);
|
||||
|
||||
|
77
decoder/rds_consts.h
Normal file
77
decoder/rds_consts.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
const char *PTY_text[] = {
|
||||
"No programme type or undefined",
|
||||
"News",
|
||||
"Current Affairs",
|
||||
"Information",
|
||||
"Sport",
|
||||
"Education",
|
||||
"Drama",
|
||||
"Culture",
|
||||
"Science",
|
||||
"Varied",
|
||||
"Pop Music",
|
||||
"Rock Music",
|
||||
"Easy Listening Music",
|
||||
"Light classical",
|
||||
"Serious classical",
|
||||
"Other Music",
|
||||
"Weather",
|
||||
"Finance",
|
||||
"Children's programmes",
|
||||
"Social Affairs",
|
||||
"Religion",
|
||||
"Phone In",
|
||||
"Travel",
|
||||
"Leisure",
|
||||
"Jazz Music",
|
||||
"Country Music",
|
||||
"National Music",
|
||||
"Oldies Music",
|
||||
"Folk Music",
|
||||
"Documentary",
|
||||
"Alarm Test",
|
||||
"Alarm"
|
||||
};
|
||||
|
||||
const char *ECC_text[] = {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
||||
/*E0*/ "DE", "DZ", "AD", "IL", "IT", "BE", "RU", "PS","AL", "AT", "HU", "MT", "DE", "" , "EG", "",
|
||||
/*E1*/ "GR", "CY", "SM", "CH", "JO", "FI", "LU", "BG","DK", "GI", "IQ", "GB", "LY", "RO", "FR", "",
|
||||
/*E2*/ "MA", "CZ", "PL", "VA", "SK", "SY", "TN", "" ,"LI", "IS", "MC", "LT", "YU", "ES", "NO", "",
|
||||
/*E3*/ "" , "IE", "TR", "MK", "" , "" , "" , "NL","LV", "LB", "" , "HR", "" , "SE", "BY", "",
|
||||
/*E4*/ "MD", "EE", "" , "" , "" , "UA", "" , "PT","SI", "" , "" , "" , "" , "" , "BA", "",
|
||||
};
|
||||
|
||||
const char *ptype_stext[] = {
|
||||
"L", "I", "N", "S", "R1", "R2", "R3", "R4", "R5", "R6","R7", "R8", "R9", "R10", "R11", "R12"
|
||||
};
|
||||
|
||||
const char *ptype_ltext[] = {
|
||||
"Local",
|
||||
"International",
|
||||
"National",
|
||||
"Supra-regional",
|
||||
"Regional 1",
|
||||
"Regional 2",
|
||||
"Regional 3",
|
||||
"Regional 4",
|
||||
"Regional 5",
|
||||
"Regional 6",
|
||||
"Regional 7",
|
||||
"Regional 8",
|
||||
"Regional 9",
|
||||
"Regional 10",
|
||||
"Regional 11",
|
||||
"Regional 12"
|
||||
};
|
||||
|
129
decoder/rds_test.c
Normal file
129
decoder/rds_test.c
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "rds.h"
|
||||
#include "tmc.h"
|
||||
|
||||
sqlite3 *lcl_db;
|
||||
int OutputFlags;
|
||||
|
||||
void test_rds_PI_cb(unsigned short PI, unsigned char ccode, unsigned char ptype, unsigned char pref, void *udata)
|
||||
{
|
||||
printf("New PI=%d ccode=%X ptype=%X '%s' '%s' pref=%d\n", PI, ccode, ptype, ptype_stext[ptype], ptype_ltext[ptype], pref);
|
||||
}
|
||||
|
||||
void test_rds_radiotext_cb(char *radio_text, void *udata)
|
||||
{
|
||||
printf("New RT: '%s'\n", radio_text);
|
||||
}
|
||||
|
||||
void test_rds_date_time_cb(struct tm *rds_time, void *udata)
|
||||
{
|
||||
printf("RDS date/time: %s", asctime(rds_time));
|
||||
}
|
||||
|
||||
void test_rds_sname_cb(char *sname, void *udata)
|
||||
{
|
||||
printf("RDS sname='%s'\n", sname);
|
||||
}
|
||||
|
||||
void test_tmc_msg_cb(char *msg, void *user_data)
|
||||
{
|
||||
printf("\nTMC msg:\n%s", msg);
|
||||
}
|
||||
|
||||
int open_radio(const char *name)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(name, O_RDONLY);
|
||||
if (fd > 0)
|
||||
return fd;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char rdevname[PATH_MAX] = "/dev/radio0";
|
||||
unsigned short rdsgroup[4];
|
||||
int rds_fd, i;
|
||||
|
||||
rds_init();
|
||||
tmc_init();
|
||||
|
||||
i = 1;
|
||||
while (i < argc) {
|
||||
if (strncmp("-all", argv[i], 4) == 0)
|
||||
OutputFlags |= ~0;
|
||||
if (strncmp("-di", argv[i], 3) == 0)
|
||||
OutputFlags |= RDS_OUTPUT_RDSINFO;
|
||||
if (strncmp("-rd", argv[i], 3) == 0)
|
||||
if (argc > i)
|
||||
strcpy(rdevname, argv[++i]);
|
||||
if (strncmp("-rt", argv[i], 3) == 0) {
|
||||
// OutputFlags |= RDS_OUTPUT_RADIO_TEXT;
|
||||
rds_set_radiotext_cb(test_rds_radiotext_cb, NULL);
|
||||
}
|
||||
if (strncmp("-ri", argv[i], 3) == 0) {
|
||||
// OutputFlags |= RDS_OUTPUT_STATION_ID;
|
||||
rds_set_sname_cb(test_rds_sname_cb, NULL);
|
||||
}
|
||||
if (strncmp("-tmc", argv[i], 4) == 0) {
|
||||
// OutputFlags |= RDS_OUTPUT_TMC;
|
||||
tmc_set_msg_cb(test_tmc_msg_cb, NULL);
|
||||
}
|
||||
if (strncmp("-eon", argv[i], 4) == 0)
|
||||
OutputFlags |= RDS_OUTPUT_EON;
|
||||
if (strncmp("-dt", argv[i], 3) == 0) {
|
||||
// OutputFlags |= RDS_OUTPUT_DATETIME;
|
||||
rds_set_date_time_cb(test_rds_date_time_cb, NULL);
|
||||
}
|
||||
if (strncmp("-ug", argv[i], 3) == 0)
|
||||
OutputFlags |= RDS_OUTPUT_UNKNGRP;
|
||||
if (strncmp("-pi", argv[i], 3) == 0) {
|
||||
// OutputFlags |= RDS_RECEIVE_INDICATOR;
|
||||
rds_set_PI_cb(test_rds_PI_cb, NULL);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!(rds_fd = open_radio(rdevname))) {
|
||||
perror("open radio:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sqlite3_open("lcl.db", &lcl_db) != SQLITE_OK) {
|
||||
perror("open LCL database:");
|
||||
close(rds_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (rds_receive_group(rds_fd, rdsgroup)) {
|
||||
/* group complete, start decode */
|
||||
rds_decode_group(rdsgroup);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
BIN
decoder/test-dumps/7.7-radio-rds.dump
Normal file
BIN
decoder/test-dumps/7.7-radio-rds.dump
Normal file
Binary file not shown.
11
decoder/test-dumps/Readme.txt
Normal file
11
decoder/test-dumps/Readme.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
These are some dumps for interoperability testing.
|
||||
|
||||
I am aware that the content is not mine - it belongs to the broadcasting
|
||||
radio stations. I do not want in any form take ownership of it. I just want
|
||||
to provide it here for the mere purpose of interoperability testing.
|
||||
|
||||
If you find any data contained herein not belonging here or should be
|
||||
removed please contact me: nicole.faerber@dpin.de
|
||||
and I will promptly take care of it.
|
||||
|
BIN
decoder/test-dumps/ch-drs1_rds.dump
Normal file
BIN
decoder/test-dumps/ch-drs1_rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/ch-drs3_rds.dump
Normal file
BIN
decoder/test-dumps/ch-drs3_rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/ch-energy_rds.dump
Normal file
BIN
decoder/test-dumps/ch-energy_rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/ch-radio24_rds.dump
Normal file
BIN
decoder/test-dumps/ch-radio24_rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/ch-rete-uno_rds.dump
Normal file
BIN
decoder/test-dumps/ch-rete-uno_rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/global-fm-rds.dump
Normal file
BIN
decoder/test-dumps/global-fm-rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/jac-fm-rds.dump
Normal file
BIN
decoder/test-dumps/jac-fm-rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/kiss_fm_rssi-77.bin
Normal file
BIN
decoder/test-dumps/kiss_fm_rssi-77.bin
Normal file
Binary file not shown.
BIN
decoder/test-dumps/mix191fm-rds.dump
Normal file
BIN
decoder/test-dumps/mix191fm-rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/r.ewtn-89.00-rds.dump
Normal file
BIN
decoder/test-dumps/r.ewtn-89.00-rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/radio-anuncia-rds.dump
Normal file
BIN
decoder/test-dumps/radio-anuncia-rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/radio-faycan-rds.dump
Normal file
BIN
decoder/test-dumps/radio-faycan-rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/radio-las-arenas-rds.dump
Normal file
BIN
decoder/test-dumps/radio-las-arenas-rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/radio-nueve-rds.dump
Normal file
BIN
decoder/test-dumps/radio-nueve-rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/radio-sol-rds.dump
Normal file
BIN
decoder/test-dumps/radio-sol-rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/radio_nova_rssi-88.bin
Normal file
BIN
decoder/test-dumps/radio_nova_rssi-88.bin
Normal file
Binary file not shown.
BIN
decoder/test-dumps/radio_yle_rssi-83.bin
Normal file
BIN
decoder/test-dumps/radio_yle_rssi-83.bin
Normal file
Binary file not shown.
59
decoder/test-dumps/rds-swr3-expanded.txt
Normal file
59
decoder/test-dumps/rds-swr3-expanded.txt
Normal file
|
@ -0,0 +1,59 @@
|
|||
Autobahnen
|
||||
|
||||
A45 Auf der A45 Aschaffenburg Richtung Gießen zwischen Florstadt und
|
||||
Wölfersheim steht ein defekter LKW, die rechte Spur ist blockiert. [AS:
|
||||
37/38] - Stand: 22:10 [Detailkarte]
|
||||
|
||||
A5 Karlsruhe Richtung Basel vor dem Grenzübergang Weil am Rhein/Basel 1 km
|
||||
LKW-Stau. [AS: 70/70] - Stand: 20:35 [Detailkarte]
|
||||
|
||||
A60 Auf der A60 Mainz Richtung Rüsselsheim ist die Ausfahrt Mainz-Weisenau
|
||||
wegen einer Baustelle bis 30.04.2010 gesperrt. Eine Umleitung ist
|
||||
eingerichtet. [AS: 23/23] - Stand: 00:56 [Detailkarte]
|
||||
|
||||
A61 Auf der A61 Koblenz Richtung Ludwigshafen ist Dreieck Nahetal wegen
|
||||
Brückenarbeiten die Überleitung zur A60 Richtung Mainz bis 13.04.2010
|
||||
gesperrt. Eine Umleitung ist eingerichtet. [AS: 50/50] - Stand: 07:36
|
||||
[Detailkarte]
|
||||
|
||||
A61 Ludwigshafen Richtung Koblenz zwischen Alzey und Gau-Bickelheim
|
||||
Dauerbaustelle, Fahrbahnverengung [AS: 52/55] - Stand: 11:07 [Detailkarte]
|
||||
|
||||
A62 Landstuhl Richtung Pirmasens wegen Instandhaltungsarbeiten im
|
||||
Hörnchenbergtunnel ist nur eine Spur frei. Eine Umleitung ist eingerichtet.
|
||||
(bis 15. Juni) - Stand: 07:42 [Detailkarte]
|
||||
|
||||
A62 An der A62 Landstuhl Richtung Pirmasens ist die Einfahrt Landstuhl-Atzel
|
||||
und der Tunnel gesperrt. Bitte folgen Sie der Umleitungsbeschilderung.
|
||||
(Dauer: bis 15. Juni) [AS: 11/11] - Stand: 07:47 [Detailkarte]
|
||||
|
||||
A661 Auf der A661 Bad Homburg - Egelsbach ist die Anschlußstelle
|
||||
Frankfurt-Heddernheim in beiden Richtungen wegen Brückenarbeiten bis etwa
|
||||
23:00 Uhr gesperrt. [AS: 6/6] - Stand: 22:06 [Detailkarte]
|
||||
|
||||
A661 Die A661 Bad Homburg Richtung Egelsbach ist in Höhe
|
||||
Frankfurt-Heddernheim wegen Brückenarbeiten bis etwa 00:15 Uhr gesperrt.
|
||||
[AS: 6/6] - Stand: 22:06 [Detailkarte]
|
||||
|
||||
A8 An der A8 Stuttgart Richtung München ist die Ausfahrt Kirchheim
|
||||
(Teck)-West wegen Instandhaltungsarbeiten bis voraussichtlich 23. April
|
||||
gesperrt. [AS: 56/56] - Stand: 23:11 [Detailkarte]
|
||||
|
||||
A8 Auf der A8 Luxemburg Richtung Saarlouis zwischen Perl und Perl-Borg ist
|
||||
die rechte Spur wegen Fahrbahnbeschädigungen nach einem LKW-Brand gesperrt.
|
||||
[AS: 3/2] - Stand: 13:05 [Detailkarte]
|
||||
Bundesstraßen
|
||||
|
||||
B10 Auf der B10 in Stuttgart zwischen Pragtunnel und Borsigstraße Baustelle,
|
||||
eine Spur ist gesperrt bis 12.04.2010 05:00 Uhr. - 20:13 [Detailkarte]
|
||||
Sonstiges
|
||||
|
||||
Im Kreis Tuttlingen ist die Ortsdurchfahrt Buchheim wegen Kanal- und
|
||||
Straßenbauarbeiten bis 01.06.2010 in beiden Richtungen gesperrt. Umleitung
|
||||
über Leibertingen-Thalheim, Leibertingen, Beuron und Fridingen. - 06:26
|
||||
[Detailkarte]
|
||||
|
||||
In Mainz auf der Zwerchallee zwischen Am Schützenweg und Rheinallee in
|
||||
beiden Richtungen bis 15.04.2010 14:00 Uhr Behinderungen durch
|
||||
Tiefbauarbeiten. Umleitungsempfehlung: über Mombacher Straße und
|
||||
Kaiser-Karl-Ring. - 16:50 [Detailkarte]
|
BIN
decoder/test-dumps/rds-swr3.dump
Normal file
BIN
decoder/test-dumps/rds-swr3.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/rds-test2.dump
Normal file
BIN
decoder/test-dumps/rds-test2.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/rds.dump
Normal file
BIN
decoder/test-dumps/rds.dump
Normal file
Binary file not shown.
BIN
decoder/test-dumps/ylex_rssi-88.bin
Normal file
BIN
decoder/test-dumps/ylex_rssi-88.bin
Normal file
Binary file not shown.
BIN
decoder/test-dumps/zon-holland-rds.dump
Normal file
BIN
decoder/test-dumps/zon-holland-rds.dump
Normal file
Binary file not shown.
569
decoder/tmc.c
Normal file
569
decoder/tmc.c
Normal file
|
@ -0,0 +1,569 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "rds.h"
|
||||
#include "bitstream.h"
|
||||
#include "tmc.h"
|
||||
#include "tmc_consts.h"
|
||||
|
||||
// #define DEBUG 1
|
||||
|
||||
static struct TMC_info tmc_info;
|
||||
static struct TMC_msg cur_tmc_msg;
|
||||
static char tmc_msg_str[4096];
|
||||
static char *tmc_msg_ptr = tmc_msg_str;
|
||||
|
||||
static struct _TMC_Private {
|
||||
void (*tmc_sid_cb)(char *sid_text, void *user_data);
|
||||
void *tmc_sid_cb_data;
|
||||
void (*tmc_msg_cb)(char *msg, void *user_data);
|
||||
void *tmc_msg_cb_data;
|
||||
} _tmc_private;
|
||||
|
||||
void tmc_init(void)
|
||||
{
|
||||
memset(&_tmc_private, 0, sizeof(struct _TMC_Private));
|
||||
}
|
||||
|
||||
void tmc_set_sid_cb(void (*tmc_sid_cb)(char *sid_text, void *user_data), void *user_data)
|
||||
{
|
||||
_tmc_private.tmc_sid_cb = tmc_sid_cb;
|
||||
_tmc_private.tmc_sid_cb_data = user_data;
|
||||
}
|
||||
|
||||
static void reset_msg_buf(void)
|
||||
{
|
||||
tmc_msg_ptr = tmc_msg_str;
|
||||
memset(tmc_msg_str, 0, 4096);
|
||||
}
|
||||
|
||||
void tmc_set_msg_cb(void (*tmc_msg_cb)(char *msg, void *user_data), void *user_data)
|
||||
{
|
||||
reset_msg_buf();
|
||||
_tmc_private.tmc_msg_cb = tmc_msg_cb;
|
||||
_tmc_private.tmc_msg_cb_data = user_data;
|
||||
}
|
||||
|
||||
void interpret_tmc(int event, int location, int extent, int CI, int direction)
|
||||
{
|
||||
char sql[256]="";
|
||||
sqlite3_stmt *ppstmt;
|
||||
int neg_off=0, pos_off=0, lin_ref=0;
|
||||
char road_nr[256]="", fst_name[256]="";
|
||||
char rdir1[256]="", rdir2[256]="";
|
||||
char last_name[256]="";
|
||||
char evt_str[256]="";
|
||||
char type_str1[128]="";
|
||||
char type_str2[128]="";
|
||||
char typechr;
|
||||
int typenr, subtype;
|
||||
|
||||
//fprintf(stderr, "mCI=%d CI=%d\n", cur_tmc_msg.CI, CI);
|
||||
if (CI != cur_tmc_msg.CI || CI == -1) {
|
||||
#ifdef DEBUG
|
||||
printf("GF evt=%d loc=%d ext=%d CI=%d dir=%d", event, location, extent, CI, direction);
|
||||
printf("ev=%d ", event);
|
||||
#endif
|
||||
snprintf(sql, 256, "select ROADNUMBER,FIRST_NAME,NEGATIVE_OFFSET,POSITIVE_OFFSET,LINEAR_REFERENCE,TYPE,SUBTYPE from lcl where LOCATIONCODE=%d", location);
|
||||
sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
|
||||
if (sqlite3_step(ppstmt) != SQLITE_ROW) {
|
||||
#ifdef DEBUG
|
||||
printf("lcl.db failed to get location %d event %d\n", location, event);
|
||||
#endif
|
||||
// return; // we cannot do much without location
|
||||
} else {
|
||||
neg_off = sqlite3_column_int(ppstmt, 2);
|
||||
pos_off = sqlite3_column_int(ppstmt, 3);
|
||||
lin_ref = sqlite3_column_int(ppstmt, 4);
|
||||
strncpy(road_nr, (char *)sqlite3_column_text(ppstmt, 0), 255);
|
||||
strncpy(fst_name, (char *)sqlite3_column_text(ppstmt, 1), 255);
|
||||
strncpy(type_str1, (char *)sqlite3_column_text(ppstmt, 5), 128);
|
||||
subtype = sqlite3_column_int(ppstmt, 6);
|
||||
typechr = type_str1[0];
|
||||
typenr = atoi(&type_str1[1]);
|
||||
#ifdef DEBUG
|
||||
printf(" type=%c %d stype=%d\n", typechr, typenr, subtype);
|
||||
#endif
|
||||
snprintf(sql, 256, "select SNATDESC from lcl_supc where CLASS='%c' AND TCD=%d AND STCD=%d", typechr, typenr, subtype);
|
||||
sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
|
||||
if (sqlite3_step(ppstmt) != SQLITE_ROW) {
|
||||
#ifdef DEBUG
|
||||
printf("lcl.db failed to get suptype %c %d stype=%d\n", typechr, typenr, subtype);
|
||||
#endif
|
||||
} else {
|
||||
strncpy(type_str1, (char *)sqlite3_column_text(ppstmt, 0), 128);
|
||||
}
|
||||
if (lin_ref != 0) {
|
||||
snprintf(sql, 256, "select FIRST_NAME,SECOND_NAME from lcl where LOCATIONCODE=%d", lin_ref);
|
||||
sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
|
||||
if (sqlite3_step(ppstmt) != SQLITE_ROW) {
|
||||
#ifdef DEBUG
|
||||
printf("lcl.db failed to get linear ref %d\n", lin_ref);
|
||||
#endif
|
||||
} else {
|
||||
if (!direction) {
|
||||
strncpy(rdir1, (char *)sqlite3_column_text(ppstmt, 1), 255);
|
||||
strncpy(rdir2, (char *)sqlite3_column_text(ppstmt, 0), 255);
|
||||
} else {
|
||||
strncpy(rdir1, (char *)sqlite3_column_text(ppstmt, 0), 255);
|
||||
strncpy(rdir2, (char *)sqlite3_column_text(ppstmt, 1), 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (extent > 0) {
|
||||
// printf("%s ab %s ", road_nr, fst_name);
|
||||
if (direction) {
|
||||
/* neg */
|
||||
while (extent--) {
|
||||
snprintf(sql, 256, "select ROADNUMBER,FIRST_NAME,NEGATIVE_OFFSET,POSITIVE_OFFSET,TYPE,SUBTYPE from lcl where LOCATIONCODE=%d", neg_off);
|
||||
sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
|
||||
if (sqlite3_step(ppstmt) != SQLITE_ROW) {
|
||||
#ifdef DEBUG
|
||||
printf("lcl.db failed to get location %d\n", location);
|
||||
#endif
|
||||
break;
|
||||
} else
|
||||
neg_off = sqlite3_column_int(ppstmt, 2);
|
||||
}
|
||||
//printf("bis %s\n", (char *)sqlite3_column_text(ppstmt, 1));
|
||||
} else {
|
||||
/* pos */
|
||||
while (extent--) {
|
||||
snprintf(sql, 256, "select ROADNUMBER,FIRST_NAME,NEGATIVE_OFFSET,POSITIVE_OFFSET,TYPE,SUBTYPE from lcl where LOCATIONCODE=%d", pos_off);
|
||||
sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
|
||||
if (sqlite3_step(ppstmt) != SQLITE_ROW) {
|
||||
#ifdef DEBUG
|
||||
printf("lcl.db failed to get location %d\n", location);
|
||||
#endif
|
||||
break;
|
||||
} else
|
||||
pos_off = sqlite3_column_int(ppstmt, 3);
|
||||
}
|
||||
//printf("bis %s\n", (char *)sqlite3_column_text(ppstmt, 1));
|
||||
}
|
||||
strncpy(last_name, (char *)sqlite3_column_text(ppstmt, 1), 255);
|
||||
extent=1;
|
||||
strncpy(type_str2, (char *)sqlite3_column_text(ppstmt, 4), 128);
|
||||
subtype = sqlite3_column_int(ppstmt, 5);
|
||||
typechr = type_str2[0];
|
||||
typenr = atoi(&type_str2[1]);
|
||||
// printf(" type=%c %d stype=%d\n", typechr, typenr, subtype);
|
||||
snprintf(sql, 256, "select SNATDESC from lcl_supc where CLASS='%c' AND TCD=%d AND STCD=%d", typechr, typenr, subtype);
|
||||
sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
|
||||
if (sqlite3_step(ppstmt) != SQLITE_ROW) {
|
||||
#ifdef DEBUG
|
||||
printf("lcl.db failed to get suptype %c %d stype=%d\n", typechr, typenr, subtype);
|
||||
#endif
|
||||
} else {
|
||||
strncpy(type_str2, (char *)sqlite3_column_text(ppstmt, 0), 128);
|
||||
}
|
||||
}
|
||||
}
|
||||
snprintf(sql, 256, "select TEXT_DE_NOQUANT from evl where CODE=%d", event);
|
||||
sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
|
||||
if (sqlite3_step(ppstmt) != SQLITE_ROW) {
|
||||
#ifdef DEBUG
|
||||
printf("lcl.db failed to get event %d\n", event);
|
||||
#endif
|
||||
} else {
|
||||
strncpy(evt_str, (char *)sqlite3_column_text(ppstmt, 0), 255);
|
||||
}
|
||||
if (CI != -1)
|
||||
cur_tmc_msg.CI = CI;
|
||||
|
||||
// here goes the message ;)
|
||||
if (lin_ref != 0)
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s %s - %s,\n", road_nr, rdir1, rdir2);
|
||||
if (location == 64777) {
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s\n", evt_str);
|
||||
} else {
|
||||
if (extent) {
|
||||
#ifdef DEBUG
|
||||
printf("dir=%d\n", direction);
|
||||
#endif
|
||||
if (direction)
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, "zwischen %s %s und %s %s\n%s\n", type_str1, fst_name, type_str2, last_name, evt_str);
|
||||
else
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, "zwischen %s %s und %s %s\n%s\n", type_str2, last_name, type_str1, fst_name, evt_str);
|
||||
} else
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, "in Höhe %s %s\n%s\n", type_str1, fst_name, evt_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parse_tmc_single(unsigned short *rdsgroup)
|
||||
{
|
||||
unsigned short event = cur_tmc_msg.msgs[0][1] & 0x7FF;
|
||||
unsigned short location = cur_tmc_msg.msgs[0][2];
|
||||
unsigned char extent = (cur_tmc_msg.msgs[0][1] & ((0x07) << 11)) >> 11;
|
||||
unsigned char dir = (cur_tmc_msg.msgs[0][1] & ((0x01) << 14)) >> 14;
|
||||
static unsigned short oevent=0;
|
||||
static unsigned short olocation=0;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf(" single group\n");
|
||||
#endif
|
||||
if (olocation != location && oevent != event) {
|
||||
interpret_tmc(event, location, extent, -1, dir);
|
||||
olocation = location;
|
||||
oevent = event;
|
||||
if (_tmc_private.tmc_msg_cb != NULL) {
|
||||
_tmc_private.tmc_msg_cb(tmc_msg_str, _tmc_private.tmc_msg_cb_data);
|
||||
}
|
||||
reset_msg_buf();
|
||||
}
|
||||
}
|
||||
|
||||
char *replace_str(char *str, char *orig, char *rep)
|
||||
{
|
||||
static char buffer[4096];
|
||||
char *p;
|
||||
|
||||
if(!(p = strstr(str, orig))) // Is 'orig' even in 'str'?
|
||||
return str;
|
||||
|
||||
strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$
|
||||
buffer[p-str] = '\0';
|
||||
|
||||
sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void interpret_tmc_multi(void)
|
||||
{
|
||||
int i;
|
||||
unsigned short event = cur_tmc_msg.msgs[0][1] & 0x7FF;
|
||||
unsigned short location = cur_tmc_msg.msgs[0][2];
|
||||
unsigned char extent = (cur_tmc_msg.msgs[0][1] & (0x07 << 11)) >> 11;
|
||||
unsigned char dir = (cur_tmc_msg.msgs[0][1] & (0x01 << 14)) >> 14;
|
||||
unsigned char label;
|
||||
unsigned short aevent;
|
||||
char sql[256]="";
|
||||
sqlite3_stmt *ppstmt;
|
||||
char evt_str[256]="";
|
||||
char lofrstr[16]="";
|
||||
unsigned char ext_buf[256];
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("interpret_tmc_multi: sz=%d\n", cur_tmc_msg.msgsz);
|
||||
for (i=0; i<cur_tmc_msg.msgsz; i++) {
|
||||
printf(" #%d 0x%04x 0x%04x 0x%04x\n", i, cur_tmc_msg.msgs[i][0], cur_tmc_msg.msgs[i][1], cur_tmc_msg.msgs[i][2]);
|
||||
}
|
||||
|
||||
printf("TMC event=%d loc=%d ext=%d dir=%d\n", event, location, extent, dir);
|
||||
#endif
|
||||
|
||||
interpret_tmc(event, location, extent, -1, dir);
|
||||
|
||||
|
||||
for (i=0; i < cur_tmc_msg.msgsz; i++) {
|
||||
ext_buf[i*4] = (cur_tmc_msg.msgs[i+1][1] & 0xff00) >> 8;
|
||||
ext_buf[(i*4)+1] = cur_tmc_msg.msgs[i+1][1] & 0xff;
|
||||
ext_buf[(i*4)+2] = (cur_tmc_msg.msgs[i+1][2] & 0xff00) >> 8;
|
||||
ext_buf[(i*4)+3] = cur_tmc_msg.msgs[i+1][2] & 0xff;
|
||||
};
|
||||
bitstream_start(ext_buf, cur_tmc_msg.msgsz*4);
|
||||
|
||||
bitstream_get_next_bits(4);
|
||||
|
||||
while (bitstream_bits_remaining() > 0) {
|
||||
label = bitstream_get_next_bits(4);
|
||||
#ifdef DEBUG
|
||||
if (OutputFlags & RDS_OUTPUT_TMC) {
|
||||
printf("o '%s' %x\n", EVNT_LABEL[label], label);
|
||||
}
|
||||
#endif
|
||||
if (label == 0) {
|
||||
unsigned char dur=0;
|
||||
|
||||
dur = bitstream_get_next_bits(3);
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " dur=%d\n", dur);
|
||||
}
|
||||
if (label == 1) {
|
||||
unsigned char ctrlcd=0;
|
||||
|
||||
ctrlcd = bitstream_get_next_bits(3);
|
||||
if (ctrlcd == 2)
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, "in beiden Richtungen\n");
|
||||
else
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " ctrlcd=%d\n", ctrlcd);
|
||||
}
|
||||
if (label == 2) {
|
||||
unsigned int lofr=0;
|
||||
|
||||
lofr = bitstream_get_next_bits(5);
|
||||
if (lofr == 0) {
|
||||
printf("shit - more than 100km\n");
|
||||
strcpy(lofrstr, ">100 km");
|
||||
}
|
||||
if (lofr>0 && lofr<11) {
|
||||
printf("lofr = %dkm\n", lofr);
|
||||
sprintf(lofrstr, "%d km", lofr);
|
||||
}
|
||||
if (lofr>10 && lofr<16) {
|
||||
printf("lofr = %dkm\n", 12 + ((lofr-11)*2));
|
||||
sprintf(lofrstr, "%d km", 12 + ((lofr-11)*2));
|
||||
}
|
||||
if (lofr>15 && lofr<32)
|
||||
printf("lofr = %dkm\n", 25 + ((lofr-16)*5));
|
||||
sprintf(lofrstr, "%d km", 25 + ((lofr-16)*5));
|
||||
}
|
||||
if (label == 3) {
|
||||
unsigned int slim=0;
|
||||
|
||||
slim = bitstream_get_next_bits(5);
|
||||
if (slim > 0)
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " slim = %d km/h\n", slim*5);
|
||||
}
|
||||
if (label == 4) {
|
||||
unsigned int quant5=0;
|
||||
|
||||
quant5 = bitstream_get_next_bits(5);
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " quant5 = %d km\n", quant5);
|
||||
}
|
||||
if (label == 5) {
|
||||
unsigned int quant8=0;
|
||||
|
||||
quant8 = bitstream_get_next_bits(8);
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " quant8 = %d km\n", quant8);
|
||||
}
|
||||
if (label == 6) {
|
||||
unsigned char supinfo=0;
|
||||
|
||||
supinfo = bitstream_get_next_bits(8);
|
||||
#ifdef DEBUG
|
||||
printf(" supinfo = %d\n", supinfo);
|
||||
#endif
|
||||
snprintf(sql, 256, "select TEXT_DE from ev_sup where CODE=%d", supinfo);
|
||||
sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
|
||||
if (sqlite3_step(ppstmt) != SQLITE_ROW) {
|
||||
#ifdef DEBUG
|
||||
printf("lcl.db failed to get ev_sup %d\n", supinfo);
|
||||
#endif
|
||||
} else {
|
||||
strncpy(evt_str, (char *)sqlite3_column_text(ppstmt, 0), 255);
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s\n", evt_str);
|
||||
}
|
||||
}
|
||||
if (label == 7) {
|
||||
unsigned int stime=0;
|
||||
|
||||
stime = bitstream_get_next_bits(8);
|
||||
// printf(" stime = %d\n", stime);
|
||||
if (stime < 96) {
|
||||
stime *= 15; // 15 min internval
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab %02d:%02d\n", (stime / 60), (stime % 60));
|
||||
} else if (stime < 201) {
|
||||
stime -= 96;
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab %d Tage, %d:00 Uhr\n", (stime / 24), (stime % 24));
|
||||
} else if (stime < 232) {
|
||||
stime -= 201;
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab dem %d.\n", stime+1);
|
||||
} else {
|
||||
stime -= 232;
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab %s. %s\n", (stime % 2) ? "31":"15", monthname[stime / 2]);
|
||||
}
|
||||
}
|
||||
if (label == 8) {
|
||||
unsigned int etime=0;
|
||||
|
||||
etime = bitstream_get_next_bits(8);
|
||||
// printf(" etime = %d\n", etime);
|
||||
if (etime < 96) {
|
||||
etime *= 15; // 15 min internval
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis vor. %02d:%02d\n", (etime / 60), (etime % 60));
|
||||
} else if (etime < 201) {
|
||||
etime -= 96;
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis %d Tage, %d:00 Uhr\n", (etime / 24), (etime % 24));
|
||||
} else if (etime < 232) {
|
||||
etime -= 201;
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis zum %d.\n", etime+1);
|
||||
} else {
|
||||
etime -= 232;
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis etwa %s. %s\n", (etime % 2) ? "31":"15", monthname[etime / 2]);
|
||||
}
|
||||
}
|
||||
if (label == 9) {
|
||||
aevent = bitstream_get_next_bits(11);
|
||||
#ifdef DEBUG
|
||||
printf(" aevent = %d ", aevent);
|
||||
#endif
|
||||
snprintf(sql, 256, "select TEXT_DE_NOQUANT from evl where CODE=%d", aevent);
|
||||
sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
|
||||
if (sqlite3_step(ppstmt) != SQLITE_ROW) {
|
||||
#ifdef DEBUG
|
||||
printf("-lcl.db failed to get event %d\n", aevent);
|
||||
#endif
|
||||
} else {
|
||||
strncpy(evt_str, (char *)sqlite3_column_text(ppstmt, 0), 255);
|
||||
// printf("(L)='%s'\n", evt_str);
|
||||
if (strlen(lofrstr) != 0 && strstr(evt_str,"(L)")!=NULL) {
|
||||
replace_str(evt_str, "(L)", lofrstr);
|
||||
}
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s\n", evt_str);
|
||||
}
|
||||
}
|
||||
if (label == 10) {
|
||||
unsigned char div;
|
||||
|
||||
div = bitstream_get_next_bits(16);
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " div = %d\n", div);
|
||||
}
|
||||
if (label == 11) {
|
||||
unsigned char dest;
|
||||
|
||||
dest = bitstream_get_next_bits(16);
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " dest = %d\n", dest);
|
||||
}
|
||||
if (label == 12) {
|
||||
unsigned char res;
|
||||
|
||||
res = bitstream_get_next_bits(16);
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " res = %d\n", res);
|
||||
}
|
||||
if (label == 13) {
|
||||
unsigned char clink;
|
||||
|
||||
clink = bitstream_get_next_bits(16);
|
||||
tmc_msg_ptr += sprintf(tmc_msg_ptr, " clink = %d\n", clink);
|
||||
}
|
||||
if (label == 14) {
|
||||
// tmc_msg_ptr += sprintf(tmc_msg_ptr, " seperator\n");
|
||||
}
|
||||
if (label == 15) {
|
||||
#ifdef DEBUG
|
||||
printf(" res\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifdef DEBUGx
|
||||
if (cur_tmc_msg.msgsz > 2) {
|
||||
for (i=2; i<cur_tmc_msg.msgsz; i++) {
|
||||
printf(" #%d 0x%04x 0x%04x 0x%04x\n", i, cur_tmc_msg.msgs[i][0], cur_tmc_msg.msgs[i][1], cur_tmc_msg.msgs[i][2]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (_tmc_private.tmc_msg_cb != NULL) {
|
||||
_tmc_private.tmc_msg_cb(tmc_msg_str, _tmc_private.tmc_msg_cb_data);
|
||||
}
|
||||
reset_msg_buf();
|
||||
}
|
||||
|
||||
void parse_tmc_multi(unsigned short *rdsgroup)
|
||||
{
|
||||
unsigned char GSI = (rdsgroup[2] & 0x3000) >> 12;
|
||||
static unsigned char oGSI = 0;
|
||||
#ifdef DEBUG
|
||||
unsigned char CI = rdsgroup[1] & 0x07;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
printf(" multi CI=%d ", CI);
|
||||
#endif
|
||||
if (rdsgroup[2] & 0x8000) {
|
||||
if (cur_tmc_msg.msgsz > 0) {
|
||||
interpret_tmc_multi();
|
||||
cur_tmc_msg.msgsz = 0;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf(" #1 GSI=%d\n", GSI);
|
||||
#endif
|
||||
oGSI = 0;
|
||||
memset(&cur_tmc_msg.msgs, 0, 6*3*sizeof(short));
|
||||
cur_tmc_msg.msgs[0][0] = rdsgroup[1];
|
||||
cur_tmc_msg.msgs[0][1] = rdsgroup[2];
|
||||
cur_tmc_msg.msgs[0][2] = rdsgroup[3];
|
||||
//msgsz = 1;
|
||||
} else {
|
||||
if (rdsgroup[2] & 0x4000) {
|
||||
// printf(" #2 GSI=%d\n", GSI);
|
||||
cur_tmc_msg.msgs[1][0] = rdsgroup[1];
|
||||
cur_tmc_msg.msgs[1][1] = rdsgroup[2];
|
||||
cur_tmc_msg.msgs[1][2] = rdsgroup[3];
|
||||
cur_tmc_msg.msgsz = 2;
|
||||
} else {
|
||||
if (oGSI == 0) {
|
||||
oGSI = GSI;
|
||||
}
|
||||
// printf(" #%d GSI=%d (oGSI=%d)\n", 2+(oGSI-GSI), GSI, oGSI);
|
||||
cur_tmc_msg.msgs[2+(oGSI-GSI)][0] = rdsgroup[1];
|
||||
cur_tmc_msg.msgs[2+(oGSI-GSI)][1] = rdsgroup[2];
|
||||
cur_tmc_msg.msgs[2+(oGSI-GSI)][2] = rdsgroup[3];
|
||||
cur_tmc_msg.msgsz = 2+(oGSI-GSI);
|
||||
}
|
||||
}
|
||||
// printf(" 0x%04x 0x%04x\n", rdsgroup[2], rdsgroup[3]);
|
||||
}
|
||||
|
||||
// enum TMCtype { TMC_GROUP=0, TMC_SINGLE, TMC_SYSTEM, TMC_TUNING };
|
||||
|
||||
void decode_tmc(unsigned short *rdsgroup)
|
||||
{
|
||||
unsigned char X4 = (rdsgroup[1] & 0x10) >> 4;
|
||||
unsigned char X3 = (rdsgroup[1] & 0x08) >> 3;
|
||||
unsigned char X0X2 = rdsgroup[1] & 0x07;
|
||||
//static unsigned char tmc_provider[9] = "";
|
||||
|
||||
if (X4 == 0) { // User message
|
||||
if (X3 == 1) { // single group msg
|
||||
parse_tmc_single(rdsgroup);
|
||||
} else { // multi group msg
|
||||
if (X0X2 > 0) {
|
||||
parse_tmc_multi(rdsgroup);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else
|
||||
printf("err... X0X2(CI) = %d\n", X0X2);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
if (X3 == 1) {
|
||||
//printf(" tuning?\n");
|
||||
} else {
|
||||
// printf(" system ");
|
||||
if (X0X2 == 4) {
|
||||
tmc_info.provider_str[0] = (rdsgroup[2] & 0xff00) >> 8;
|
||||
tmc_info.provider_str[1] = rdsgroup[2] & 0xff;
|
||||
tmc_info.provider_str[2] = (rdsgroup[3] & 0xff00) >> 8;
|
||||
tmc_info.provider_str[3] = rdsgroup[3] & 0xff;
|
||||
if (_tmc_private.tmc_sid_cb != NULL)
|
||||
_tmc_private.tmc_sid_cb(tmc_info.provider_str, _tmc_private.tmc_sid_cb_data);
|
||||
if (OutputFlags & RDS_OUTPUT_TMC)
|
||||
printf("provider = '%s'\n", tmc_info.provider_str);
|
||||
} else if (X0X2 == 5) {
|
||||
tmc_info.provider_str[4] = (rdsgroup[2] & 0xff00) >> 8;
|
||||
tmc_info.provider_str[5] = rdsgroup[2] & 0xff;
|
||||
tmc_info.provider_str[6] = (rdsgroup[3] & 0xff00) >> 8;
|
||||
tmc_info.provider_str[7] = rdsgroup[3] & 0xff;
|
||||
if (_tmc_private.tmc_sid_cb != NULL)
|
||||
_tmc_private.tmc_sid_cb(tmc_info.provider_str, _tmc_private.tmc_sid_cb_data);
|
||||
if (OutputFlags & RDS_OUTPUT_TMC)
|
||||
printf("provider = '%s'\n", tmc_info.provider_str);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else
|
||||
printf("X0X2(CI)=%d \n", X0X2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
decoder/tmc.h
Normal file
34
decoder/tmc.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
struct TMC_info {
|
||||
char provider_str[9];
|
||||
};
|
||||
|
||||
struct TMC_msg {
|
||||
int CI;
|
||||
int neg_off, pos_off, lin_ref;
|
||||
char road_nr[256], fst_name[256];
|
||||
char rdir1[256], rdir2[256];
|
||||
char last_name[256];
|
||||
char evt_str[256];
|
||||
unsigned char msgsz;
|
||||
unsigned short msgs[6][3];
|
||||
};
|
||||
|
||||
extern sqlite3 *lcl_db;
|
||||
|
||||
void decode_tmc(unsigned short *rdsgroup);
|
||||
|
||||
void tmc_init(void);
|
||||
|
||||
void tmc_set_sid_cb(void (*tmc_sid_cb)(char *sid_text, void *user_data), void *user_data);
|
||||
void tmc_set_msg_cb(void (*tmc_msg_cb)(char *msg, void *user_data), void *user_data);
|
||||
|
46
decoder/tmc_consts.h
Normal file
46
decoder/tmc_consts.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
const char *EVNT_LABEL[] = {
|
||||
"Duration",
|
||||
"Control code",
|
||||
"Length of route affected",
|
||||
"Speed limit advice",
|
||||
"Quantifier",
|
||||
"Quantifier",
|
||||
"Supplimetary information code",
|
||||
"Explicit start time",
|
||||
"Explicit stop time",
|
||||
"Additional event",
|
||||
"Detailed diversion instructions",
|
||||
"Destination",
|
||||
"reserved",
|
||||
"Cross linkage to source of problem, on another route",
|
||||
"Separator",
|
||||
"reserved"
|
||||
};
|
||||
|
||||
const char *monthname[] = {
|
||||
"Januar",
|
||||
"Februar",
|
||||
"März",
|
||||
"April",
|
||||
"Mai",
|
||||
"Juni",
|
||||
"Juli",
|
||||
"August",
|
||||
"September",
|
||||
"Oktober",
|
||||
"November",
|
||||
"Dezember",
|
||||
"Oh oh..."
|
||||
};
|
||||
|
||||
|
227
decoder/uberradio.c
Normal file
227
decoder/uberradio.c
Normal file
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "rds.h"
|
||||
#include "tmc.h"
|
||||
|
||||
sqlite3 *lcl_db;
|
||||
int OutputFlags;
|
||||
|
||||
typedef struct {
|
||||
int radio_fd;
|
||||
guint r_input_id;
|
||||
GtkBuilder *builder;
|
||||
GtkWidget *MainWin;
|
||||
GtkWidget *RDS_TP_Label;
|
||||
GtkWidget *RDS_TA_Label;
|
||||
GtkWidget *RDS_M_Label;
|
||||
GtkWidget *RDS_S_Label;
|
||||
GtkWidget *RDS_SID_Label;
|
||||
GtkWidget *RDS_RT_Label;
|
||||
GtkWidget *RDS_PTY_Label;
|
||||
GtkWidget *RDS_Date_Time_Label;
|
||||
GtkWidget *RDS_PI_Label;
|
||||
GtkWidget *RDS_PType_Label;
|
||||
GtkWidget *TMC_View;
|
||||
GtkTextBuffer *TMC_MSG_Buffer;
|
||||
} uberradio_ui;
|
||||
|
||||
|
||||
int open_radio(const char *name)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(name, O_RDONLY | O_NONBLOCK);
|
||||
if (fd > 0)
|
||||
return fd;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void handle_r_input(gpointer user_data, gint source, GdkInputCondition condition)
|
||||
{
|
||||
uberradio_ui *rui = (uberradio_ui *)user_data;
|
||||
static unsigned short rdsgroup[4];
|
||||
|
||||
if (rds_receive_group(rui->radio_fd, rdsgroup)) {
|
||||
rds_decode_group(rdsgroup);
|
||||
}
|
||||
}
|
||||
|
||||
void rds_sname_cb(char *sname, void *user_data)
|
||||
{
|
||||
uberradio_ui *rui = (uberradio_ui *)user_data;
|
||||
static char pbuf[128];
|
||||
|
||||
snprintf(pbuf, 128, "<tt>%s</tt>", sname);
|
||||
gtk_label_set_markup(GTK_LABEL(rui->RDS_SID_Label), pbuf);
|
||||
}
|
||||
|
||||
|
||||
void rds_radiotext_cb(char *rtext, void *user_data)
|
||||
{
|
||||
uberradio_ui *rui = (uberradio_ui *)user_data;
|
||||
static char pbuf[128];
|
||||
|
||||
snprintf(pbuf, 128, "<tt>%s</tt>", rtext);
|
||||
gtk_label_set_markup(GTK_LABEL(rui->RDS_RT_Label), pbuf);
|
||||
}
|
||||
|
||||
void rds_sinfo_cb(unsigned char TP, unsigned char TA, unsigned char MS, unsigned char PTY, void *user_data)
|
||||
{
|
||||
uberradio_ui *rui = (uberradio_ui *)user_data;
|
||||
|
||||
if (TP) {
|
||||
gtk_widget_set_sensitive(rui->RDS_TP_Label, TRUE);
|
||||
} else {
|
||||
gtk_widget_set_sensitive(rui->RDS_TP_Label, FALSE);
|
||||
}
|
||||
if (TA) {
|
||||
gtk_widget_set_sensitive(rui->RDS_TA_Label, TRUE);
|
||||
} else {
|
||||
gtk_widget_set_sensitive(rui->RDS_TA_Label, FALSE);
|
||||
}
|
||||
if (MS) {
|
||||
gtk_widget_set_sensitive(rui->RDS_M_Label, TRUE);
|
||||
gtk_widget_set_sensitive(rui->RDS_S_Label, FALSE);
|
||||
} else {
|
||||
gtk_widget_set_sensitive(rui->RDS_M_Label, FALSE);
|
||||
gtk_widget_set_sensitive(rui->RDS_S_Label, TRUE);
|
||||
}
|
||||
gtk_label_set_text(GTK_LABEL(rui->RDS_PTY_Label), PTY_text[PTY]);
|
||||
}
|
||||
|
||||
void rds_date_time_cb(struct tm *rds_time, void *user_data)
|
||||
{
|
||||
uberradio_ui *rui = (uberradio_ui *)user_data;
|
||||
char pbuf[128];
|
||||
char pbuf2[128];
|
||||
gchar *pstr;
|
||||
GDate mgdate;
|
||||
|
||||
mgdate.julian = 0;
|
||||
mgdate.dmy = 1;
|
||||
mgdate.day = rds_time->tm_mday;
|
||||
mgdate.month = rds_time->tm_mon + 1;
|
||||
mgdate.year = rds_time->tm_year + 1900;
|
||||
g_date_strftime(pbuf, 128, "%A %d. %B %Y ", &mgdate);
|
||||
strftime(pbuf2, 128, "%R", rds_time);
|
||||
pstr = g_strconcat(pbuf, pbuf2, NULL);
|
||||
gtk_label_set_markup(GTK_LABEL(rui->RDS_Date_Time_Label), pstr);
|
||||
g_free(pstr);
|
||||
}
|
||||
|
||||
void rds_PI_cb(unsigned short PI, unsigned char ccode, unsigned char ptype, unsigned char pref, void *user_data)
|
||||
{
|
||||
uberradio_ui *rui = (uberradio_ui *)user_data;
|
||||
char pbuf[128];
|
||||
|
||||
// we get this callback if PI changed which also means that
|
||||
// the station changed, which again means that all previous
|
||||
// information in the display is invalid now
|
||||
|
||||
gtk_widget_set_sensitive(rui->RDS_TP_Label, FALSE);
|
||||
gtk_widget_set_sensitive(rui->RDS_TA_Label, FALSE);
|
||||
gtk_widget_set_sensitive(rui->RDS_M_Label, FALSE);
|
||||
gtk_widget_set_sensitive(rui->RDS_S_Label, FALSE);
|
||||
gtk_label_set_markup(GTK_LABEL(rui->RDS_RT_Label), "");
|
||||
gtk_label_set_text(GTK_LABEL(rui->RDS_PTY_Label), "");
|
||||
gtk_label_set_markup(GTK_LABEL(rui->RDS_SID_Label), "");
|
||||
gtk_text_buffer_set_text(rui->TMC_MSG_Buffer, "", -1);
|
||||
|
||||
snprintf(pbuf, 128, "<b>PI</b> %d", PI);
|
||||
gtk_label_set_markup(GTK_LABEL(rui->RDS_PI_Label), pbuf);
|
||||
gtk_label_set_markup(GTK_LABEL(rui->RDS_PType_Label), ptype_ltext[ptype]);
|
||||
}
|
||||
|
||||
void tmc_msg_cb(char *msg, void *user_data)
|
||||
{
|
||||
uberradio_ui *rui = (uberradio_ui *)user_data;
|
||||
|
||||
gtk_text_buffer_set_text(rui->TMC_MSG_Buffer, msg, -1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
GtkWidget *w;
|
||||
uberradio_ui rui;
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
rui.builder = gtk_builder_new();
|
||||
if (!gtk_builder_add_from_file(rui.builder, "uberradio.ui", NULL)) {
|
||||
fprintf(stderr, "Error creating GtkBuilder\n");
|
||||
return 0;
|
||||
}
|
||||
gtk_builder_connect_signals(rui.builder, NULL);
|
||||
|
||||
rui.RDS_TP_Label = GTK_WIDGET(gtk_builder_get_object(rui.builder, "RDS_TP_Label"));
|
||||
gtk_widget_set_sensitive(rui.RDS_TP_Label, FALSE);
|
||||
rui.RDS_TA_Label = GTK_WIDGET(gtk_builder_get_object(rui.builder, "RDS_TA_Label"));
|
||||
gtk_widget_set_sensitive(rui.RDS_TA_Label, FALSE);
|
||||
rui.RDS_M_Label = GTK_WIDGET(gtk_builder_get_object(rui.builder, "RDS_M_Label"));
|
||||
gtk_widget_set_sensitive(rui.RDS_M_Label, FALSE);
|
||||
rui.RDS_S_Label = GTK_WIDGET(gtk_builder_get_object(rui.builder, "RDS_S_Label"));
|
||||
gtk_widget_set_sensitive(rui.RDS_S_Label, FALSE);
|
||||
rui.RDS_SID_Label = GTK_WIDGET(gtk_builder_get_object(rui.builder, "RDS_SID_Label"));
|
||||
rui.RDS_RT_Label = GTK_WIDGET(gtk_builder_get_object(rui.builder, "RDS_RT_Label"));
|
||||
rui.RDS_PTY_Label = GTK_WIDGET(gtk_builder_get_object(rui.builder, "RDS_PTY_Label"));
|
||||
rui.RDS_Date_Time_Label = GTK_WIDGET(gtk_builder_get_object(rui.builder, "RDS_Date_Time_Label"));
|
||||
rui.RDS_PI_Label = GTK_WIDGET(gtk_builder_get_object(rui.builder, "RDS_PI_Label"));
|
||||
rui.RDS_PType_Label = GTK_WIDGET(gtk_builder_get_object(rui.builder, "RDS_PType_Label"));
|
||||
|
||||
rui.TMC_View = GTK_WIDGET(gtk_builder_get_object(rui.builder, "TMC_View"));
|
||||
rui.TMC_MSG_Buffer = GTK_TEXT_BUFFER(gtk_builder_get_object(rui.builder, "TMC_MSG_Buffer"));
|
||||
|
||||
rui.MainWin = GTK_WIDGET(gtk_builder_get_object(rui.builder, "MainWin"));
|
||||
|
||||
gtk_widget_show_all(rui.MainWin);
|
||||
|
||||
if (!(rui.radio_fd = open_radio("/dev/radio0"))) {
|
||||
perror("open radio:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sqlite3_open("lcl.db", &lcl_db) != SQLITE_OK) {
|
||||
perror("open lcl.db");
|
||||
close(rui.radio_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rds_init();
|
||||
tmc_init();
|
||||
|
||||
rui.r_input_id = gdk_input_add (rui.radio_fd, GDK_INPUT_READ, handle_r_input, &rui);
|
||||
|
||||
rds_set_sname_cb(rds_sname_cb, &rui);
|
||||
rds_set_radiotext_cb(rds_radiotext_cb, &rui);
|
||||
rds_set_date_time_cb(rds_date_time_cb, &rui);
|
||||
rds_set_sinfo_cb(rds_sinfo_cb, &rui);
|
||||
rds_set_PI_cb(rds_PI_cb, &rui);
|
||||
|
||||
tmc_set_msg_cb(tmc_msg_cb, &rui);
|
||||
|
||||
gtk_main();
|
||||
|
||||
return 0;
|
||||
}
|
209
decoder/uberradio.ui
Normal file
209
decoder/uberradio.ui
Normal file
|
@ -0,0 +1,209 @@
|
|||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy project-wide -->
|
||||
<object class="GtkWindow" id="MainWin">
|
||||
<signal name="destroy" handler="gtk_main_quit"/>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="Radio_Freq">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><big><b>???.?? MHz</b></big></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_SID_Label">
|
||||
<property name="visible">True</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="width_chars">10</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox2">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_PI_Label">
|
||||
<property name="visible">True</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_Ccode_Label">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_PType_Label">
|
||||
<property name="visible">True</property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_TP_Label">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">TP</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_TA_Label">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">TA</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_M_Label">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">M</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_S_Label">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">S</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_PTY_Label">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_Date_Time_Label">
|
||||
<property name="visible">True</property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="RDS_RT_Label">
|
||||
<property name="visible">True</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="width_chars">66</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox3">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTextView" id="TMC_View">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="wrap_mode">word</property>
|
||||
<property name="cursor_visible">False</property>
|
||||
<property name="buffer">TMC_MSG_Buffer</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="TMC_Frame_Label">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><b>TMC</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkTextBuffer" id="TMC_MSG_Buffer"/>
|
||||
</interface>
|
Loading…
Add table
Add a link
Reference in a new issue