From ad766936dcb0ca76a8e372a39539b310abfa6b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=B6ck?= Date: Sat, 12 Mar 2016 00:57:21 +0100 Subject: [PATCH 1/7] Initial implementation of aa preemphasis Implemented as a first-order highpass filter --- src/fm_mpx.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/fm_mpx.c b/src/fm_mpx.c index fd0b7fa..fc639dc 100644 --- a/src/fm_mpx.c +++ b/src/fm_mpx.c @@ -66,6 +66,12 @@ float fir_buffer_stereo[FIR_SIZE] = {0}; int fir_index = 0; int channels; +float *last_buffer_val; +float preemphasis_corner_freq; +float preemphasis_prewarp; +float preemphasis_coefficient_a; +float preemphasis_coefficient_b; + SNDFILE *inf; @@ -114,8 +120,18 @@ int fm_mpx_open(char *filename, size_t len) { printf("%d channels, generating stereo multiplex.\n", channels); } else { printf("1 channel, monophonic operation.\n"); - } - + } + + // Create the preemphasis + last_buffer_val = (float*) malloc(sizeof(float)*channels); + for(int i=0;i Date: Sat, 12 Mar 2016 17:32:38 +0100 Subject: [PATCH 2/7] Code cleanup whitespaces instead of tabs, see ad766936dcb0ca76a8e372a39539b310abfa6b50 --- src/fm_mpx.c | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/fm_mpx.c b/src/fm_mpx.c index fc639dc..3ab6530 100644 --- a/src/fm_mpx.c +++ b/src/fm_mpx.c @@ -121,17 +121,17 @@ int fm_mpx_open(char *filename, size_t len) { } else { printf("1 channel, monophonic operation.\n"); } - - // Create the preemphasis - last_buffer_val = (float*) malloc(sizeof(float)*channels); - for(int i=0;i Date: Sat, 12 Mar 2016 17:36:47 +0100 Subject: [PATCH 3/7] low-pass filter now has 22050Hz cutoff Might not apply to the fm standards, but since you are not supposed to use it publicly, it doesn't matter --- src/fm_mpx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fm_mpx.c b/src/fm_mpx.c index 3ab6530..0d4ac85 100644 --- a/src/fm_mpx.c +++ b/src/fm_mpx.c @@ -134,8 +134,8 @@ int fm_mpx_open(char *filename, size_t len) { // Create the low-pass FIR filter - float cutoff_freq = 15000 * .8; - if(in_samplerate/2 < cutoff_freq) cutoff_freq = in_samplerate/2 * .8; + float cutoff_freq = 22050; + if(in_samplerate/2 < cutoff_freq) cutoff_freq = in_samplerate/2; From 2cc5d96e62870961e926732d519609faba6e9872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=B6ck?= Date: Sat, 12 Mar 2016 18:16:45 +0100 Subject: [PATCH 4/7] switched the filter to a much better sounding one previouse filter was described by: y[n] = bx[n]-bx[n-1]+ay[n-1] filter is now described by: y(n) = x(n) - a x(n-1) with 'a' = old 'b' even i have no idea if that coefficient 'a' is selected correctly, it sounds much better than before --- src/fm_mpx.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/fm_mpx.c b/src/fm_mpx.c index 0d4ac85..011b484 100644 --- a/src/fm_mpx.c +++ b/src/fm_mpx.c @@ -66,11 +66,13 @@ float fir_buffer_stereo[FIR_SIZE] = {0}; int fir_index = 0; int channels; +//3.185kHz for Europe, 2.120kHz for US +const float PREEMPHASIS_US = 2120; +const float PREEMPHASIS_EU = 3185; float *last_buffer_val; float preemphasis_corner_freq; float preemphasis_prewarp; -float preemphasis_coefficient_a; -float preemphasis_coefficient_b; +float preemphasis_coefficient; SNDFILE *inf; @@ -126,11 +128,10 @@ int fm_mpx_open(char *filename, size_t len) { last_buffer_val = (float*) malloc(sizeof(float)*channels); for(int i=0;i Date: Sat, 12 Mar 2016 19:05:39 +0100 Subject: [PATCH 5/7] preemphasis is now configurable from CLI by adding -preemph [eu/us/{cutoff freq}] to your Pi FM parameters, you can now select the right preemphasis for your region --- src/fm_mpx.c | 9 ++------- src/fm_mpx.h | 2 +- src/pi_fm_rds.c | 20 ++++++++++++++++---- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/fm_mpx.c b/src/fm_mpx.c index 011b484..44bf32f 100644 --- a/src/fm_mpx.c +++ b/src/fm_mpx.c @@ -66,11 +66,7 @@ float fir_buffer_stereo[FIR_SIZE] = {0}; int fir_index = 0; int channels; -//3.185kHz for Europe, 2.120kHz for US -const float PREEMPHASIS_US = 2120; -const float PREEMPHASIS_EU = 3185; float *last_buffer_val; -float preemphasis_corner_freq; float preemphasis_prewarp; float preemphasis_coefficient; @@ -88,7 +84,7 @@ float *alloc_empty_buffer(size_t length) { } -int fm_mpx_open(char *filename, size_t len) { +int fm_mpx_open(char *filename, size_t len, float preemphasis_corner_freq) { length = len; if(filename != NULL) { @@ -128,10 +124,9 @@ int fm_mpx_open(char *filename, size_t len) { last_buffer_val = (float*) malloc(sizeof(float)*channels); for(int i=0;i. */ -extern int fm_mpx_open(char *filename, size_t len); +extern int fm_mpx_open(char *filename, size_t len, float preemphasis_corner_freq); extern int fm_mpx_get_samples(float *mpx_buffer); extern int fm_mpx_close(); \ No newline at end of file diff --git a/src/pi_fm_rds.c b/src/pi_fm_rds.c index b8e91b8..da8765e 100644 --- a/src/pi_fm_rds.c +++ b/src/pi_fm_rds.c @@ -191,7 +191,6 @@ // (broadcast radio) and about 3.5 for NBFM (walkie-talkie style radio) #define DEVIATION 25.0 - typedef struct { uint32_t info, src, dst, length, stride, next, pad[2]; @@ -311,7 +310,7 @@ map_peripheral(uint32_t base, uint32_t len) #define DATA_SIZE 5000 -int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, float ppm, char *control_pipe) { +int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, float ppm, char *control_pipe, float preemphasis_cutoff) { // Catch all signals possible - it is vital we kill the DMA engine // on process exit! for (int i = 0; i < 64; i++) { @@ -446,7 +445,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, int data_index = 0; // Initialize the baseband generator - if(fm_mpx_open(audio_file, DATA_SIZE) < 0) return 1; + if(fm_mpx_open(audio_file, DATA_SIZE, preemphasis_cutoff) < 0) return 1; // Initialize the RDS modulator char myps[9] = {0}; @@ -537,6 +536,8 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, return 0; } +#define PREEMPHASIS_EU 3185 +#define PREEMPHASIS_US 2120 int main(int argc, char **argv) { char *audio_file = NULL; @@ -546,6 +547,7 @@ int main(int argc, char **argv) { char *rt = "PiFmRds: live FM-RDS transmission from the RaspberryPi"; uint16_t pi = 0x1234; float ppm = 0; + float preemphasis_cutoff = PREEMPHASIS_US; // Parse command-line arguments @@ -578,6 +580,16 @@ int main(int argc, char **argv) { } else if(strcmp("-ctl", arg)==0 && param != NULL) { i++; control_pipe = param; + } else if(strcmp("-preemph", arg)==0 && param != NULL) { + i++; + if(strcmp("eu", param)==0) { + preemphasis_cutoff = PREEMPHASIS_EU; + } else if(strcmp("us", param)==0) { + preemphasis_cutoff = PREEMPHASIS_US; + } + else { + preemphasis_cutoff = atof(param); + } } else { fatal("Unrecognised argument: %s.\n" "Syntax: pi_fm_rds [-freq freq] [-audio file] [-ppm ppm_error] [-pi pi_code]\n" @@ -585,7 +597,7 @@ int main(int argc, char **argv) { } } - int errcode = tx(carrier_freq, audio_file, pi, ps, rt, ppm, control_pipe); + int errcode = tx(carrier_freq, audio_file, pi, ps, rt, ppm, control_pipe, preemphasis_cutoff); terminate(errcode); } From 43d9431d30700545e01d0b58bd2906b430c8bb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=B6ck?= Date: Sat, 12 Mar 2016 19:34:02 +0100 Subject: [PATCH 6/7] The lowpass cutoff frequency is now configurable via CLI with -cutoff [compliant/quality/{cutoff freq}] you can now select the cutoff frequency --- src/fm_mpx.c | 4 +--- src/fm_mpx.h | 2 +- src/pi_fm_rds.c | 20 +++++++++++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/fm_mpx.c b/src/fm_mpx.c index 44bf32f..82a62c0 100644 --- a/src/fm_mpx.c +++ b/src/fm_mpx.c @@ -84,7 +84,7 @@ float *alloc_empty_buffer(size_t length) { } -int fm_mpx_open(char *filename, size_t len, float preemphasis_corner_freq) { +int fm_mpx_open(char *filename, size_t len, float cutoff_freq, float preemphasis_corner_freq) { length = len; if(filename != NULL) { @@ -130,11 +130,9 @@ int fm_mpx_open(char *filename, size_t len, float preemphasis_corner_freq) { // Create the low-pass FIR filter - float cutoff_freq = 22050; if(in_samplerate/2 < cutoff_freq) cutoff_freq = in_samplerate/2; - low_pass_fir[FIR_HALF_SIZE-1] = 2 * cutoff_freq / 228000 /2; // Here we divide this coefficient by two because it will be counted twice // when applying the filter diff --git a/src/fm_mpx.h b/src/fm_mpx.h index bd07e4e..6efa54c 100644 --- a/src/fm_mpx.h +++ b/src/fm_mpx.h @@ -21,6 +21,6 @@ along with this program. If not, see . */ -extern int fm_mpx_open(char *filename, size_t len, float preemphasis_corner_freq); +extern int fm_mpx_open(char *filename, size_t len, float cutoff_freq, float preemphasis_corner_freq); extern int fm_mpx_get_samples(float *mpx_buffer); extern int fm_mpx_close(); \ No newline at end of file diff --git a/src/pi_fm_rds.c b/src/pi_fm_rds.c index da8765e..bad24ad 100644 --- a/src/pi_fm_rds.c +++ b/src/pi_fm_rds.c @@ -310,7 +310,7 @@ map_peripheral(uint32_t base, uint32_t len) #define DATA_SIZE 5000 -int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, float ppm, char *control_pipe, float preemphasis_cutoff) { +int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, float ppm, char *control_pipe, float cutoff, float preemphasis_cutoff) { // Catch all signals possible - it is vital we kill the DMA engine // on process exit! for (int i = 0; i < 64; i++) { @@ -445,7 +445,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, int data_index = 0; // Initialize the baseband generator - if(fm_mpx_open(audio_file, DATA_SIZE, preemphasis_cutoff) < 0) return 1; + if(fm_mpx_open(audio_file, DATA_SIZE, cutoff, preemphasis_cutoff) < 0) return 1; // Initialize the RDS modulator char myps[9] = {0}; @@ -539,6 +539,9 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt, #define PREEMPHASIS_EU 3185 #define PREEMPHASIS_US 2120 +#define CUTOFF_COMPLIANT 15000 +#define CUTOFF_QUALITY 22050 + int main(int argc, char **argv) { char *audio_file = NULL; char *control_pipe = NULL; @@ -547,6 +550,7 @@ int main(int argc, char **argv) { char *rt = "PiFmRds: live FM-RDS transmission from the RaspberryPi"; uint16_t pi = 0x1234; float ppm = 0; + float cutoff = CUTOFF_COMPLIANT; float preemphasis_cutoff = PREEMPHASIS_US; @@ -590,6 +594,16 @@ int main(int argc, char **argv) { else { preemphasis_cutoff = atof(param); } + } else if(strcmp("-cutoff", arg)==0 && param != NULL) { + i++; + if(strcmp("compliant", param)==0) { + cutoff = CUTOFF_COMPLIANT; + } else if(strcmp("quality", param)==0) { + cutoff = CUTOFF_QUALITY; + } + else { + cutoff = atof(param); + } } else { fatal("Unrecognised argument: %s.\n" "Syntax: pi_fm_rds [-freq freq] [-audio file] [-ppm ppm_error] [-pi pi_code]\n" @@ -597,7 +611,7 @@ int main(int argc, char **argv) { } } - int errcode = tx(carrier_freq, audio_file, pi, ps, rt, ppm, control_pipe, preemphasis_cutoff); + int errcode = tx(carrier_freq, audio_file, pi, ps, rt, ppm, control_pipe, cutoff, preemphasis_cutoff); terminate(errcode); } From a0328e969054badb48d5f881331d906cd4deb542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=B6ck?= Date: Sat, 12 Mar 2016 20:15:17 +0100 Subject: [PATCH 7/7] updated readme to match changes --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aa9c8df..487911f 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ To test stereophonic audio, you can try the file `stereo_44100.wav` provided. The more general syntax for running Pi-FM-RDS is as follows: ``` -pi_fm_rds [-freq freq] [-audio file] [-ppm ppm_error] [-pi pi_code] [-ps ps_text] [-rt rt_text] +pi_fm_rds [-freq freq] [-audio file] [-ppm ppm_error] [-pi pi_code] [-ps ps_text] [-rt rt_text] [-cutoff cutoff_freq] [-preemph preemphasis_mode] ``` All arguments are optional: @@ -63,6 +63,8 @@ All arguments are optional: * `-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. +* `-cutoff` specifies the cutoff frequency (in Hz, 'compliant' for 15,000Hz or 'quality' for 22,050Hz) used by Pi-FM-RDS' internal lowpass filter. Values greater than 15000 are not compliant. Use carefully. +* `-preemph` specifies which preemph should be used, since it differs from location. For Europe choose 'eu', for the US choose 'us'. 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.