initial commit to new repo
This commit is contained in:
parent
09bc9a5042
commit
42e3bad75a
54 changed files with 2924 additions and 1 deletions
15
README.md
15
README.md
|
@ -1,3 +1,16 @@
|
|||
# rdstmc
|
||||
|
||||
application to decode RDS and TMC data from an RDS data stream
|
||||
application to decode RDS and TMC data from an RDS data stream
|
||||
This is a small project to RDS data reception and decoding.
|
||||
Do not expect anything dramatically great or extremely usable :)
|
||||
|
||||
decoder/
|
||||
Code for decoding RDS and TMC data streams
|
||||
|
||||
driver-n900/
|
||||
Enhancement to the Nokia N900 FM radio driver to
|
||||
actually output the raw RDS data in V4L compliant format
|
||||
to the radio device (/dev/radio1) - kernel diff and
|
||||
ready compiled kernel module (because it was nasty to get
|
||||
the compile setup right - just for your convenience)
|
||||
|
||||
|
|
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>
|
356
driver-n900/COPYING
Normal file
356
driver-n900/COPYING
Normal file
|
@ -0,0 +1,356 @@
|
|||
|
||||
NOTE! This copyright does *not* cover user programs that use kernel
|
||||
services by normal system calls - this is merely considered normal use
|
||||
of the kernel, and does *not* fall under the heading of "derived work".
|
||||
Also note that the GPL below is copyrighted by the Free Software
|
||||
Foundation, but the instance of code that it refers to (the Linux
|
||||
kernel) is copyrighted by me and others who actually wrote it.
|
||||
|
||||
Also note that the only valid version of the GPL as far as the kernel
|
||||
is concerned is _this_ particular version of the license (ie v2, not
|
||||
v2.2 or v3.x or whatever), unless explicitly otherwise stated.
|
||||
|
||||
Linus Torvalds
|
||||
|
||||
----------------------------------------
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
5
driver-n900/Readme.txt
Normal file
5
driver-n900/Readme.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
Patch against release kernel source and binary kernel module (drop-in
|
||||
replacement) to enable V4L compliant RDS data output on Nokia N900, comes
|
||||
through /dev/radio1 (/dev/radio0 is the FM transmitter).
|
||||
|
191
driver-n900/radio-bcm2048-2.6.28.diff
Normal file
191
driver-n900/radio-bcm2048-2.6.28.diff
Normal file
|
@ -0,0 +1,191 @@
|
|||
--- ../linux-2.6.28/drivers/media/radio/radio-bcm2048.c 2010-02-18 00:59:33.000000000 +0100
|
||||
+++ kernel-2.6.28/drivers/media/radio/radio-bcm2048.c 2010-05-24 22:59:54.000000000 +0200
|
||||
@@ -6,6 +6,8 @@
|
||||
* Copyright (C) Nokia Corporation
|
||||
* Contact: Eero Nurkkala <ext-eero.nurkkala@nokia.com>
|
||||
*
|
||||
+ * Copyright (C) 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
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
@@ -21,6 +23,16 @@
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
+/*
|
||||
+ * History:
|
||||
+ * Eero Nurkkala <ext-eero.nurkkala@nokia.com>
|
||||
+ * Version 0.0.1
|
||||
+ * - Initial implementation
|
||||
+ * 2010-02-21 Nicole Faerber <nicole.faerber@dpin.de>
|
||||
+ * Version 0.0.2
|
||||
+ * - Add support for interrupt driven rds data reading
|
||||
+ */
|
||||
+
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
@@ -283,6 +295,12 @@
|
||||
u8 fifo_size;
|
||||
u8 scan_state;
|
||||
u8 mute_state;
|
||||
+
|
||||
+ /* for rds data device read */
|
||||
+ wait_queue_head_t read_queue;
|
||||
+ unsigned int users;
|
||||
+ unsigned char rds_data_available;
|
||||
+ unsigned int rd_index;
|
||||
};
|
||||
|
||||
static int radio_nr = -1; /* radio device minor (-1 ==> auto assign) */
|
||||
@@ -1756,6 +1774,8 @@
|
||||
bcm2048_parse_rds_ps(bdev);
|
||||
|
||||
mutex_unlock(&bdev->mutex);
|
||||
+
|
||||
+ wake_up_interruptible(&bdev->read_queue);
|
||||
}
|
||||
|
||||
static int bcm2048_get_rds_data(struct bcm2048_device *bdev, char *data)
|
||||
@@ -1869,6 +1889,11 @@
|
||||
|
||||
err = bcm2048_set_power_state(bdev, BCM2048_POWER_OFF);
|
||||
|
||||
+ init_waitqueue_head(&bdev->read_queue);
|
||||
+ bdev->rds_data_available = 0;
|
||||
+ bdev->rd_index = 0;
|
||||
+ bdev->users = 0;
|
||||
+
|
||||
unlock:
|
||||
return err;
|
||||
}
|
||||
@@ -1903,7 +1928,8 @@
|
||||
bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1,
|
||||
flags);
|
||||
}
|
||||
-
|
||||
+ bdev->rds_data_available = 1;
|
||||
+ bdev->rd_index = 0; /* new data, new start */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2139,6 +2165,100 @@
|
||||
return err;
|
||||
}
|
||||
|
||||
+
|
||||
+static int bcm2048_fops_open(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ struct bcm2048_device *bdev = video_drvdata(file);
|
||||
+
|
||||
+ bdev->users++;
|
||||
+ bdev->rd_index = 0;
|
||||
+ bdev->rds_data_available = 0;
|
||||
+
|
||||
+return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm2048_fops_release(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ struct bcm2048_device *bdev = video_drvdata(file);
|
||||
+
|
||||
+ bdev->users--;
|
||||
+
|
||||
+return 0;
|
||||
+}
|
||||
+
|
||||
+static unsigned int bcm2048_fops_poll(struct file *file,
|
||||
+ struct poll_table_struct *pts)
|
||||
+{
|
||||
+ struct bcm2048_device *bdev = video_drvdata(file);
|
||||
+ int retval = 0;
|
||||
+
|
||||
+ poll_wait(file, &bdev->read_queue, pts);
|
||||
+
|
||||
+ if (bdev->rds_data_available) {
|
||||
+ retval = POLLIN | POLLRDNORM;
|
||||
+ }
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+
|
||||
+static ssize_t bcm2048_fops_read(struct file *file, char __user *buf,
|
||||
+ size_t count, loff_t *ppos)
|
||||
+{
|
||||
+ struct bcm2048_device *bdev = video_drvdata(file);
|
||||
+ int i;
|
||||
+ int retval = 0;
|
||||
+
|
||||
+ /* we return at least 3 bytes, one block */
|
||||
+ count = (count / 3) * 3; /* only multiples of 3 */
|
||||
+ if (count < 3)
|
||||
+ return -ENOBUFS;
|
||||
+
|
||||
+ while (!bdev->rds_data_available) {
|
||||
+ if (file->f_flags & O_NONBLOCK) {
|
||||
+ retval = -EWOULDBLOCK;
|
||||
+ goto done;
|
||||
+ }
|
||||
+ //interruptible_sleep_on(&bdev->read_queue);
|
||||
+ if (wait_event_interruptible(bdev->read_queue,
|
||||
+ bdev->rds_data_available) < 0) {
|
||||
+ retval = -EINTR;
|
||||
+ goto done;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ mutex_lock(&bdev->mutex);
|
||||
+ /* copy data to userspace */
|
||||
+ i = bdev->fifo_size - bdev->rd_index;
|
||||
+ if (count > i)
|
||||
+ count = (i / 3) * 3;
|
||||
+
|
||||
+ i = 0;
|
||||
+ while (i < count) {
|
||||
+ unsigned char tmpbuf[3];
|
||||
+ tmpbuf[i] = bdev->rds_info.radio_text[bdev->rd_index+i+2];
|
||||
+ tmpbuf[i+1] = bdev->rds_info.radio_text[bdev->rd_index+i+1];
|
||||
+ tmpbuf[i+2] = ((bdev->rds_info.radio_text[bdev->rd_index+i] & 0xf0) >> 4);
|
||||
+ if ((bdev->rds_info.radio_text[bdev->rd_index+i] & BCM2048_RDS_CRC_MASK) == BCM2048_RDS_CRC_UNRECOVARABLE)
|
||||
+ tmpbuf[i+2] |= 0x80;
|
||||
+ if (copy_to_user(buf+i, tmpbuf, 3)) {
|
||||
+ retval = -EFAULT;
|
||||
+ break;
|
||||
+ };
|
||||
+ i += 3;
|
||||
+ }
|
||||
+
|
||||
+ bdev->rd_index += i;
|
||||
+ if (bdev->rd_index >= bdev->fifo_size)
|
||||
+ bdev->rds_data_available = 0;
|
||||
+
|
||||
+ mutex_unlock(&bdev->mutex);
|
||||
+ if (retval == 0)
|
||||
+ retval = i;
|
||||
+
|
||||
+done:
|
||||
+ return retval;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* bcm2048_fops - file operations interface
|
||||
*/
|
||||
@@ -2147,6 +2267,11 @@
|
||||
.llseek = no_llseek,
|
||||
.ioctl = video_ioctl2,
|
||||
.compat_ioctl = v4l_compat_ioctl32,
|
||||
+ /* for RDS read support */
|
||||
+ .open = bcm2048_fops_open,
|
||||
+ .release = bcm2048_fops_release,
|
||||
+ .read = bcm2048_fops_read,
|
||||
+ .poll = bcm2048_fops_poll
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2609,4 +2734,4 @@
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR(BCM2048_DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(BCM2048_DRIVER_DESC);
|
||||
-MODULE_VERSION("0.0.1");
|
||||
+MODULE_VERSION("0.0.2");
|
BIN
driver-n900/radio-bcm2048.ko
Normal file
BIN
driver-n900/radio-bcm2048.ko
Normal file
Binary file not shown.
43
tuning/Makefile
Normal file
43
tuning/Makefile
Normal file
|
@ -0,0 +1,43 @@
|
|||
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 tuning
|
||||
SOURCES = $(patsubst %,%.c,$(RDS_MEMBERS))
|
||||
OBJS = $(patsubst %,%.o,$(RDS_MEMBERS))
|
||||
DEPS = $(patsubst %,%.d,$(RDS_MEMBERS))
|
||||
|
||||
UR_MEMBERS = rds bitstream tmc tuning
|
||||
UR_SOURCES = $(patsubst %,%.c,$(UR_MEMBERS))
|
||||
UR_OBJS = $(patsubst %,%.o,$(UR_MEMBERS))
|
||||
UR_DEPS = $(patsubst %,%.d,$(UR_MEMBERS))
|
||||
|
||||
all: tuning
|
||||
|
||||
tuning: $(OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.d tuning
|
||||
|
||||
-include $(DEPS)
|
1
tuning/bitstream.c
Symbolic link
1
tuning/bitstream.c
Symbolic link
|
@ -0,0 +1 @@
|
|||
../decoder/bitstream.c
|
1
tuning/bitstream.h
Symbolic link
1
tuning/bitstream.h
Symbolic link
|
@ -0,0 +1 @@
|
|||
../decoder/bitstream.h
|
1
tuning/rds.c
Symbolic link
1
tuning/rds.c
Symbolic link
|
@ -0,0 +1 @@
|
|||
../decoder/rds.c
|
1
tuning/rds.h
Symbolic link
1
tuning/rds.h
Symbolic link
|
@ -0,0 +1 @@
|
|||
../decoder/rds.h
|
1
tuning/rds_consts.h
Symbolic link
1
tuning/rds_consts.h
Symbolic link
|
@ -0,0 +1 @@
|
|||
../decoder/rds_consts.h
|
1
tuning/tmc.c
Symbolic link
1
tuning/tmc.c
Symbolic link
|
@ -0,0 +1 @@
|
|||
../decoder/tmc.c
|
1
tuning/tmc.h
Symbolic link
1
tuning/tmc.h
Symbolic link
|
@ -0,0 +1 @@
|
|||
../decoder/tmc.h
|
1
tuning/tmc_consts.h
Symbolic link
1
tuning/tmc_consts.h
Symbolic link
|
@ -0,0 +1 @@
|
|||
../decoder/tmc_consts.h
|
152
tuning/tuning.c
Normal file
152
tuning/tuning.c
Normal file
|
@ -0,0 +1,152 @@
|
|||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "rds.h"
|
||||
#include "tmc.h"
|
||||
|
||||
sqlite3 *lcl_db;
|
||||
int OutputFlags;
|
||||
|
||||
unsigned char pi_received = 0;
|
||||
unsigned char sid_received = 0;
|
||||
|
||||
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);
|
||||
pi_received = 1;
|
||||
}
|
||||
|
||||
void test_rds_sname_cb(char *sname, void *udata)
|
||||
{
|
||||
printf("RDS sname='%s'\n", sname);
|
||||
sid_received = 1;
|
||||
}
|
||||
|
||||
|
||||
void station_scan(int fd)
|
||||
{
|
||||
int ret;
|
||||
struct v4l2_frequency v4lfreq;
|
||||
struct v4l2_tuner v4ltuner;
|
||||
unsigned short rdsgroup[4];
|
||||
|
||||
v4ltuner.index = 0;
|
||||
ret = ioctl(fd, VIDIOC_G_TUNER, &v4ltuner);
|
||||
if (ret < 0)
|
||||
return;
|
||||
printf("tuner=%d\n", v4ltuner.index);
|
||||
if (v4ltuner.type == V4L2_TUNER_RADIO)
|
||||
printf(" is a radio tuner\n");
|
||||
printf("name='%s'\n", v4ltuner.name);
|
||||
|
||||
v4lfreq.tuner = v4ltuner.index;
|
||||
memset(&v4lfreq.reserved, 0, 32);
|
||||
ret = ioctl(fd, VIDIOC_G_FREQUENCY, &v4lfreq);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
v4lfreq.frequency = v4ltuner.rangelow;
|
||||
while (v4lfreq.frequency <= v4ltuner.rangehigh) {
|
||||
ret = ioctl(fd, VIDIOC_S_FREQUENCY, &v4lfreq);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret = ioctl(fd, VIDIOC_G_FREQUENCY, &v4lfreq);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret = ioctl(fd, VIDIOC_G_TUNER, &v4ltuner);
|
||||
if (ret < 0)
|
||||
break;
|
||||
printf("%lf %d %d %s %s\n", (double)v4lfreq.frequency * 62.5 / 1000000,
|
||||
v4lfreq.frequency,
|
||||
v4ltuner.signal,
|
||||
v4ltuner.rxsubchans & V4L2_TUNER_SUB_STEREO ? "stereo" : "mono",
|
||||
v4ltuner.rxsubchans & V4L2_TUNER_SUB_RDS ? "RDS" : "noRDS");
|
||||
|
||||
if (v4ltuner.signal > 30000) {
|
||||
/* seems to be a strong signal, so try RDS */
|
||||
pi_received = 0;
|
||||
sid_received = 0;
|
||||
rds_radio_retuned();
|
||||
while (pi_received == 0 || sid_received == 0) {
|
||||
if (rds_receive_group(fd, rdsgroup)) {
|
||||
/* group complete, start decode */
|
||||
rds_decode_group(rdsgroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v4lfreq.frequency += 1600; /* 1600 for .1MHz steps, 800 for 0.05MHz */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fd, ret;
|
||||
struct v4l2_capability v4lcap;
|
||||
struct v4l2_frequency v4lfreq;
|
||||
struct v4l2_tuner v4ltuner;
|
||||
|
||||
fd = open ("/dev/radio0", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
printf("error opening fd\n");
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, VIDIOC_QUERYCAP, &v4lcap);
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
|
||||
printf("driver = '%s'\n", v4lcap.driver);
|
||||
printf("card = '%s'\n", v4lcap.card);
|
||||
printf("bus_info = '%s'\n", v4lcap.bus_info);
|
||||
printf("cap && RADIO = %s\n", (v4lcap.capabilities & V4L2_CAP_RADIO) ? "yes" : "no");
|
||||
printf("cap && TUNER = %s\n", (v4lcap.capabilities & V4L2_CAP_TUNER) ? "yes" : "no");
|
||||
printf("cap && RDS = %s\n", (v4lcap.capabilities & V4L2_CAP_RDS_CAPTURE) ? "yes" : "no");
|
||||
|
||||
v4ltuner.index = 0;
|
||||
ret = ioctl(fd, VIDIOC_G_TUNER, &v4ltuner);
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
printf("tuner=%d\n", v4ltuner.index);
|
||||
if (v4ltuner.type == V4L2_TUNER_RADIO)
|
||||
printf(" is a radio tuner\n");
|
||||
printf("name='%s'\n", v4ltuner.name);
|
||||
|
||||
v4lfreq.tuner = v4ltuner.index;
|
||||
memset(&v4lfreq.reserved, 0, 32);
|
||||
ret = ioctl(fd, VIDIOC_G_FREQUENCY, &v4lfreq);
|
||||
|
||||
if (ret >= 0) {
|
||||
if (v4ltuner.capability & V4L2_TUNER_CAP_LOW) {
|
||||
printf("range %3.2lf MHz - %3.2lf MHz\n", (double)v4ltuner.rangelow * 62.5 / 1000000, (double)v4ltuner.rangehigh * 62.5 / 1000000);
|
||||
printf("freq = %3.2lf MHz\n", (double)v4lfreq.frequency * 62.5 / 1000000);
|
||||
} else
|
||||
printf("freq = %lf kHz\n", (double)v4lfreq.frequency * 62.5);
|
||||
}
|
||||
printf("signal: %d %s\n", v4ltuner.signal, v4ltuner.rxsubchans & V4L2_TUNER_SUB_STEREO ? "stereo" : "mono");
|
||||
printf("RDS signal present: %s\n", v4ltuner.rxsubchans & V4L2_TUNER_SUB_RDS ? "yes" : "no");
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "-s")==0) {
|
||||
rds_init();
|
||||
tmc_init();
|
||||
rds_set_sname_cb(test_rds_sname_cb, NULL);
|
||||
rds_set_PI_cb(test_rds_PI_cb, NULL);
|
||||
station_scan(fd);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue