From 068df065c2148fd7932e4b4c3f67bdf59b70df4b Mon Sep 17 00:00:00 2001 From: Nils Faerber Date: Wed, 23 Mar 2011 16:57:07 +0100 Subject: [PATCH] Add radio tuning test app --- decoder/rds.c | 21 +++++- decoder/rds.h | 1 + tuning/Makefile | 43 +++++++++++++ tuning/bitstream.c | 1 + tuning/bitstream.h | 1 + tuning/rds.c | 1 + tuning/rds.h | 1 + tuning/rds_consts.h | 1 + tuning/tmc.c | 1 + tuning/tmc.h | 1 + tuning/tmc_consts.h | 1 + tuning/tuning.c | 152 ++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 tuning/Makefile create mode 120000 tuning/bitstream.c create mode 120000 tuning/bitstream.h create mode 120000 tuning/rds.c create mode 120000 tuning/rds.h create mode 120000 tuning/rds_consts.h create mode 120000 tuning/tmc.c create mode 120000 tuning/tmc.h create mode 120000 tuning/tmc_consts.h create mode 100644 tuning/tuning.c diff --git a/decoder/rds.c b/decoder/rds.c index bdaee7b..ca927b2 100644 --- a/decoder/rds.c +++ b/decoder/rds.c @@ -138,10 +138,18 @@ enum RDSGroupType { GROUP_0A=0, GROUP_0B, GROUP_1A, GROUP_1B, GROUP_2A, GROUP_2B 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; @@ -172,6 +180,7 @@ int year_, mon_, K; 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]; @@ -187,10 +196,20 @@ int year_, mon_, K; 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) + 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; diff --git a/decoder/rds.h b/decoder/rds.h index 862a933..e2187f1 100644 --- a/decoder/rds.h +++ b/decoder/rds.h @@ -56,6 +56,7 @@ struct rds_info_s { }; 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); diff --git a/tuning/Makefile b/tuning/Makefile new file mode 100644 index 0000000..d018b3a --- /dev/null +++ b/tuning/Makefile @@ -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) diff --git a/tuning/bitstream.c b/tuning/bitstream.c new file mode 120000 index 0000000..bfaadd7 --- /dev/null +++ b/tuning/bitstream.c @@ -0,0 +1 @@ +../rdstmc-git/decoder/bitstream.c \ No newline at end of file diff --git a/tuning/bitstream.h b/tuning/bitstream.h new file mode 120000 index 0000000..1940845 --- /dev/null +++ b/tuning/bitstream.h @@ -0,0 +1 @@ +../rdstmc-git/decoder/bitstream.h \ No newline at end of file diff --git a/tuning/rds.c b/tuning/rds.c new file mode 120000 index 0000000..396184c --- /dev/null +++ b/tuning/rds.c @@ -0,0 +1 @@ +../rdstmc-git/decoder/rds.c \ No newline at end of file diff --git a/tuning/rds.h b/tuning/rds.h new file mode 120000 index 0000000..e7ce123 --- /dev/null +++ b/tuning/rds.h @@ -0,0 +1 @@ +../rdstmc-git/decoder/rds.h \ No newline at end of file diff --git a/tuning/rds_consts.h b/tuning/rds_consts.h new file mode 120000 index 0000000..40a669e --- /dev/null +++ b/tuning/rds_consts.h @@ -0,0 +1 @@ +../rdstmc-git/decoder/rds_consts.h \ No newline at end of file diff --git a/tuning/tmc.c b/tuning/tmc.c new file mode 120000 index 0000000..c4cd55d --- /dev/null +++ b/tuning/tmc.c @@ -0,0 +1 @@ +../rdstmc-git/decoder/tmc.c \ No newline at end of file diff --git a/tuning/tmc.h b/tuning/tmc.h new file mode 120000 index 0000000..32d7490 --- /dev/null +++ b/tuning/tmc.h @@ -0,0 +1 @@ +../rdstmc-git/decoder/tmc.h \ No newline at end of file diff --git a/tuning/tmc_consts.h b/tuning/tmc_consts.h new file mode 120000 index 0000000..75e1a20 --- /dev/null +++ b/tuning/tmc_consts.h @@ -0,0 +1 @@ +../rdstmc-git/decoder/tmc_consts.h \ No newline at end of file diff --git a/tuning/tuning.c b/tuning/tuning.c new file mode 100644 index 0000000..a4610dc --- /dev/null +++ b/tuning/tuning.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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; +} -- 2.39.2