From 20a56c47b00d1615be11309d67e6afc2c30e2803 Mon Sep 17 00:00:00 2001 From: Christophe Jacquet Date: Sun, 4 May 2014 12:41:28 +0200 Subject: [PATCH] Add the ability to receive commands on a named pipe --- README.md | 24 ++++++++++++ src/Makefile | 13 +++++-- src/control_pipe.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++ src/control_pipe.h | 30 +++++++++++++++ src/pi_fm_rds.c | 37 +++++++++++++++--- 5 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 src/control_pipe.c create mode 100644 src/control_pipe.h diff --git a/README.md b/README.md index cdcb6c9..d89778e 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ All arguments are optional: * `-pi` specifies the PI-code of the RDS broadcast. 4 hexadecimal digits. Example: `-pi FFFF`. * `-ps` specifies the station name (Program Service name, PS) of the RDS broadcast. Limit: 8 characters. Example: `-ps RASP-PI`. * `-rt` specifies the radiotext (RT) to be transmitted. Limit: 64 characters. Example: `-rt 'Hello, world!'`. +* `-ctl` specifies a named pipe (FIFO) to use as a control channel to change PS and RT at run-time (see below). * `-ppm` specifies your Raspberry Pi's oscillator error in parts per million (ppm), see below. By default the PS changes back and forth between `Pi-FmRds` and a sequence number, starting at `00000000`. The PS changes around one time per second. @@ -77,6 +78,29 @@ sox -t mp3 http://www.linuxvoice.com/episodes/lv_s02e01.mp3 -t wav - | sudo ./p ``` +### Setting PS and RT dynamically at run-time + +You can control PS and RT at run-time using a named pipe (FIFO). For this run Pi-FM-RDS with the `-ctl` argument. + +Example: + +``` +mkfifo rds_ctl +sudo ./pi_fm_rds -ctl rds_ctl +``` + +Then you can send “commands” to change the PS and RT: + +``` +cat >rds_ctl +PS MyText +RT A text to be sent as radiotext +PS OtherTxt +... +``` + + + ## Warning and Diclaimer Never use this program to transmit VHF-FM data through an antenna, as it is diff --git a/src/Makefile b/src/Makefile index e9f25ce..76cb079 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,17 +1,19 @@ CC = gcc STD_CFLAGS = -Wall -std=gnu99 -c -g -O3 -# Enable ARM-specific options only on ARM +# Enable ARM-specific options only on ARM, and compilation of the app only on ARM UNAME := $(shell uname -m) ifeq ($(UNAME), armv6l) CFLAGS = $(STD_CFLAGS) -march=armv6 -mtune=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -ffast-math + +app: rds.o waveforms.o pi_fm_rds.o fm_mpx.o control_pipe.o + $(CC) -o pi_fm_rds rds.o waveforms.o pi_fm_rds.o fm_mpx.o control_pipe.o -lm -lsndfile + else CFLAGS = $(STD_CFLAGS) endif -app: rds.o waveforms.o pi_fm_rds.o fm_mpx.o - $(CC) -o pi_fm_rds rds.o waveforms.o pi_fm_rds.o fm_mpx.o -lm -lsndfile rds_wav: rds.o waveforms.o rds_wav.o fm_mpx.o $(CC) -o rds_wav rds_wav.o rds.o waveforms.o fm_mpx.o -lm -lsndfile @@ -19,10 +21,13 @@ rds_wav: rds.o waveforms.o rds_wav.o fm_mpx.o rds.o: rds.c waveforms.h $(CC) $(CFLAGS) rds.c +control_pipe.o: control_pipe.c control_pipe.h rds.h + $(CC) $(CFLAGS) control_pipe.c + waveforms.o: waveforms.c waveforms.h $(CC) $(CFLAGS) waveforms.c -pi_fm_rds.o: pi_fm_rds.c +pi_fm_rds.o: pi_fm_rds.c control_pipe.h fm_mpx.h rds.h $(CC) $(CFLAGS) pi_fm_rds.c rds_wav.o: rds_wav.c diff --git a/src/control_pipe.c b/src/control_pipe.c new file mode 100644 index 0000000..5833c18 --- /dev/null +++ b/src/control_pipe.c @@ -0,0 +1,95 @@ +/* + PiFmRds - FM/RDS transmitter for the Raspberry Pi + Copyright (C) 2014 Christophe Jacquet, F8FTK + + See https://github.com/ChristopheJacquet/PiFmRds + + rds_wav.c is a test program that writes a RDS baseband signal to a WAV + file. It requires libsndfile. + + 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 3 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, see . + + control_pipe.c: handles command written to a non-blocking control pipe, + in order to change RDS PS and RT at runtime. +*/ + + +#include +#include +#include +#include +#include + +#include "rds.h" +#include "control_pipe.h" + +#define CTL_BUFFER_SIZE 100 + +FILE *f_ctl; + +/* + * Opens a file (pipe) to be used to control the RDS coder, in non-blocking mode. + */ +int open_control_pipe(char *filename) { + int fd = open(filename, O_RDONLY | O_NONBLOCK); + if(fd == -1) return -1; + + int flags; + flags = fcntl(fd, F_GETFL, 0); + flags |= O_NONBLOCK; + if( fcntl(fd, F_SETFL, flags) == -1 ) return -1; + + f_ctl = fdopen(fd, "r"); + if(f_ctl == NULL) return -1; + + return 0; +} + + +/* + * Polls the control file (pipe), non-blockingly, and if a command is received, + * processes it and updates the RDS data. + */ +int poll_control_pipe() { + static char buf[CTL_BUFFER_SIZE]; + + char *res = fgets(buf, CTL_BUFFER_SIZE, f_ctl); + if(res == NULL) return -1; + if(strlen(res) > 3 && res[2] == ' ') { + char *arg = res+3; + if(arg[strlen(arg)-1] == '\n') arg[strlen(arg)-1] = 0; + if(res[0] == 'P' && res[1] == 'S') { + arg[8] = 0; + set_rds_ps(arg); + printf("PS set to: \"%s\"\n", arg); + return CONTROL_PIPE_PS_SET; + } + if(res[0] == 'R' && res[1] == 'T') { + arg[64] = 0; + set_rds_rt(arg); + printf("RT set to: \"%s\"\n", arg); + return CONTROL_PIPE_RT_SET; + } + } + + return -1; +} + +/* + * Closes the control pipe. + */ +int close_control_pipe() { + if(f_ctl) return fclose(f_ctl); + else return 0; +} diff --git a/src/control_pipe.h b/src/control_pipe.h new file mode 100644 index 0000000..467116f --- /dev/null +++ b/src/control_pipe.h @@ -0,0 +1,30 @@ +/* + PiFmRds - FM/RDS transmitter for the Raspberry Pi + Copyright (C) 2014 Christophe Jacquet, F8FTK + + See https://github.com/ChristopheJacquet/PiFmRds + + rds_wav.c is a test program that writes a RDS baseband signal to a WAV + file. It requires libsndfile. + + 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 3 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, see . +*/ + + +#define CONTROL_PIPE_PS_SET 1 +#define CONTROL_PIPE_RT_SET 2 + +extern int open_control_pipe(char *filename); +extern int close_control_pipe(); +extern int poll_control_pipe(); diff --git a/src/pi_fm_rds.c b/src/pi_fm_rds.c index 10186e7..a88cd1c 100644 --- a/src/pi_fm_rds.c +++ b/src/pi_fm_rds.c @@ -101,6 +101,7 @@ #include "rds.h" #include "fm_mpx.h" +#include "control_pipe.h" #define NUM_SAMPLES 50000 @@ -205,6 +206,7 @@ terminate(int dummy) } fm_mpx_close(); + close_control_pipe(); exit(1); } @@ -262,11 +264,12 @@ map_peripheral(uint32_t base, uint32_t len) } + #define SUBSIZE 1 #define DATA_SIZE 5000 -int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, int16_t ppm) { +int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, int16_t ppm, char *control_pipe) { int i, fd, pid; char pagemap_fn[64]; @@ -424,20 +427,33 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, set_rds_rt(rt); uint16_t count = 0; uint16_t count2 = 0; + int varying_ps = 0; if(ps) { set_rds_ps(ps); printf("PI: %04X, PS: \"%s\"\n", pi, ps); } else { printf("PI: %04X, PS: \n", pi); + varying_ps = 1; } printf("RT: \"%s\"\n", rt); + // Initialize the control pipe reader + if(control_pipe) { + if(open_control_pipe(control_pipe) == 0) { + printf("Reading control commands on %s.\n", control_pipe); + } else { + printf("Failed to open control pipe: %s.\n", control_pipe); + control_pipe = NULL; + } + } + + printf("Starting to transmit on %3.1f MHz.\n", carrier_freq/1e6); for (;;) { // Default (varying) PS - if(!ps) { + if(varying_ps) { if(count == 512) { snprintf(myps, 9, "%08d", count2); set_rds_ps(myps); @@ -448,7 +464,11 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, count = 0; } count++; - } + } + + if(control_pipe && poll_control_pipe() == CONTROL_PIPE_PS_SET) { + varying_ps = 0; + } usleep(5000); @@ -493,12 +513,15 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, int main(int argc, char **argv) { char *audio_file = NULL; + char *control_pipe = NULL; uint32_t carrier_freq = 107900000; char *ps = NULL; char *rt = "PiFmRds: live FM-RDS transmission from the RaspberryPi"; uint16_t pi = 0x1234; int16_t ppm = 0; + + // Parse command-line arguments for(int i=1; i