F5OEO-PiFmRds/src/rds.c

255 wiersze
7.7 KiB
C
Czysty Zwykły widok Historia

2014-04-02 18:17:23 +00:00
/*
PiFmRds - FM/RDS transmitter for the Raspberry Pi
Copyright (C) 2014 Christophe Jacquet, F8FTK
See https://github.com/ChristopheJacquet/PiFmRds
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 <http://www.gnu.org/licenses/>.
*/
2014-03-31 22:03:10 +00:00
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
2014-03-31 22:03:10 +00:00
#include "waveforms.h"
#define RT_LENGTH 64
#define PS_LENGTH 8
2014-03-31 22:03:10 +00:00
#define GROUP_LENGTH 4
struct {
uint16_t pi;
int ta;
char ps[PS_LENGTH];
char rt[RT_LENGTH];
} rds_params = { 0 };
/* Here, the first member of the struct must be a scalar to avoid a
warning on -Wmissing-braces with GCC < 4.8.3
(bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119)
*/
2014-03-31 22:03:10 +00:00
/* The RDS error-detection code generator polynomial is
x^10 + x^8 + x^7 + x^5 + x^4 + x^3 + x^0
*/
#define POLY 0x1B9
#define POLY_DEG 10
#define MSB_BIT 0x8000
#define BLOCK_SIZE 16
#define BITS_PER_GROUP (GROUP_LENGTH * (BLOCK_SIZE+POLY_DEG))
#define SAMPLES_PER_BIT 192
2014-04-04 23:26:45 +00:00
#define FILTER_SIZE (sizeof(waveform_biphase)/sizeof(float))
#define SAMPLE_BUFFER_SIZE (SAMPLES_PER_BIT + FILTER_SIZE)
2014-03-31 22:03:10 +00:00
uint16_t offset_words[] = {0x0FC, 0x198, 0x168, 0x1B4};
// We don't handle offset word C' here for the sake of simplicity
/* Classical CRC computation */
uint16_t crc(uint16_t block) {
uint16_t crc = 0;
for(int j=0; j<BLOCK_SIZE; j++) {
int bit = (block & MSB_BIT) != 0;
block <<= 1;
int msb = (crc >> (POLY_DEG-1)) & 1;
crc <<= 1;
if((msb ^ bit) != 0) {
crc = crc ^ POLY;
}
}
return crc;
}
/* Possibly generates a CT (clock time) group if the minute has just changed
Returns 1 if the CT group was generated, 0 otherwise
*/
int get_rds_ct_group(uint16_t *blocks) {
static int latest_minutes = -1;
// Check time
time_t now;
struct tm *utc;
now = time (NULL);
utc = gmtime (&now);
if(utc->tm_min != latest_minutes) {
// Generate CT group
latest_minutes = utc->tm_min;
int l = utc->tm_mon <= 1 ? 1 : 0;
int mjd = 14956 + utc->tm_mday +
(int)((utc->tm_year - l) * 365.25) +
(int)((utc->tm_mon + 2 + l*12) * 30.6001);
blocks[1] = 0x4400 | (mjd>>15);
blocks[2] = (mjd<<1) | (utc->tm_hour>>4);
blocks[3] = (utc->tm_hour & 0xF)<<12 | utc->tm_min<<6;
utc = localtime(&now);
int offset = utc->tm_gmtoff / (30 * 60);
blocks[3] |= abs(offset);
if(offset < 0) blocks[3] |= 0x20;
2014-06-22 20:35:27 +00:00
//printf("Generated CT: %04X %04X %04X\n", blocks[1], blocks[2], blocks[3]);
return 1;
} else return 0;
}
2014-03-31 22:03:10 +00:00
/* Creates an RDS group. This generates sequences of the form 0A, 0A, 0A, 0A, 2A, etc.
2014-03-31 22:03:10 +00:00
The pattern is of length 5, the variable 'state' keeps track of where we are in the
pattern. 'ps_state' and 'rt_state' keep track of where we are in the PS (0A) sequence
2014-03-31 22:03:10 +00:00
or RT (2A) sequence, respectively.
*/
void get_rds_group(int *buffer) {
static int state = 0;
static int ps_state = 0;
static int rt_state = 0;
uint16_t blocks[GROUP_LENGTH] = {rds_params.pi, 0, 0, 0};
// Generate block content
if(! get_rds_ct_group(blocks)) { // CT (clock time) has priority on other group types
if(state < 4) {
blocks[1] = 0x0400 | ps_state;
if(rds_params.ta) blocks[1] |= 0x0010;
blocks[2] = 0xCDCD; // no AF
blocks[3] = rds_params.ps[ps_state*2]<<8 | rds_params.ps[ps_state*2+1];
ps_state++;
if(ps_state >= 4) ps_state = 0;
} else { // state == 5
blocks[1] = 0x2400 | rt_state;
blocks[2] = rds_params.rt[rt_state*4+0]<<8 | rds_params.rt[rt_state*4+1];
blocks[3] = rds_params.rt[rt_state*4+2]<<8 | rds_params.rt[rt_state*4+3];
rt_state++;
if(rt_state >= 16) rt_state = 0;
}
2014-03-31 22:03:10 +00:00
state++;
if(state >= 5) state = 0;
}
2014-03-31 22:03:10 +00:00
// Calculate the checkword for each block and emit the bits
for(int i=0; i<GROUP_LENGTH; i++) {
uint16_t block = blocks[i];
uint16_t check = crc(block) ^ offset_words[i];
for(int j=0; j<BLOCK_SIZE; j++) {
*buffer++ = ((block & (1<<(BLOCK_SIZE-1))) != 0);
block <<= 1;
}
for(int j=0; j<POLY_DEG; j++) {
*buffer++= ((check & (1<<(POLY_DEG-1))) != 0);
check <<= 1;
}
}
}
2014-04-02 18:17:23 +00:00
/* Get a number of RDS samples. This generates the envelope of the waveform using
pre-generated elementary waveform samples, and then it amplitude-modulates the
envelope with a 57 kHz carrier, which is very efficient as 57 kHz is 4 times the
sample frequency we are working at (228 kHz).
*/
2014-03-31 22:03:10 +00:00
void get_rds_samples(float *buffer, int count) {
static int bit_buffer[BITS_PER_GROUP];
static int bit_pos = BITS_PER_GROUP;
2014-04-05 17:36:06 +00:00
static float sample_buffer[SAMPLE_BUFFER_SIZE] = {0};
2014-03-31 22:03:10 +00:00
2014-04-02 18:17:23 +00:00
static int prev_output = 0;
static int cur_output = 0;
2014-03-31 22:03:10 +00:00
static int cur_bit = 0;
2014-04-04 23:26:45 +00:00
static int sample_count = SAMPLES_PER_BIT;
2014-04-02 18:17:23 +00:00
static int inverting = 0;
static int phase = 0;
2014-04-04 23:26:45 +00:00
static int in_sample_index = 0;
2014-04-05 18:02:34 +00:00
static int out_sample_index = SAMPLE_BUFFER_SIZE-1;
2014-04-04 23:26:45 +00:00
2014-03-31 22:03:10 +00:00
for(int i=0; i<count; i++) {
2014-04-04 23:26:45 +00:00
if(sample_count >= SAMPLES_PER_BIT) {
2014-03-31 22:03:10 +00:00
if(bit_pos >= BITS_PER_GROUP) {
get_rds_group(bit_buffer);
bit_pos = 0;
}
2014-04-02 18:17:23 +00:00
// do differential encoding
cur_bit = bit_buffer[bit_pos];
prev_output = cur_output;
cur_output = prev_output ^ cur_bit;
2014-04-05 17:36:06 +00:00
inverting = (cur_output == 1);
2014-04-04 23:26:45 +00:00
float *src = waveform_biphase;
2014-04-05 18:02:34 +00:00
int idx = in_sample_index;
2014-04-04 23:26:45 +00:00
for(int j=0; j<FILTER_SIZE; j++) {
2014-04-05 17:36:06 +00:00
float val = (*src++);
if(inverting) val = -val;
sample_buffer[idx++] += val;
if(idx >= SAMPLE_BUFFER_SIZE) idx = 0;
2014-04-04 23:26:45 +00:00
}
2014-04-04 23:26:45 +00:00
in_sample_index += SAMPLES_PER_BIT;
2014-04-05 17:36:06 +00:00
if(in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE;
2014-03-31 22:03:10 +00:00
bit_pos++;
2014-04-04 23:26:45 +00:00
sample_count = 0;
2014-04-02 18:17:23 +00:00
}
2014-04-05 18:02:34 +00:00
float sample = sample_buffer[out_sample_index];
sample_buffer[out_sample_index] = 0;
out_sample_index++;
2014-04-05 17:36:06 +00:00
if(out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0;
2014-04-02 18:17:23 +00:00
2014-04-05 18:02:34 +00:00
2014-04-02 18:17:23 +00:00
// modulate at 57 kHz
// use phase for this
switch(phase) {
case 0:
case 2: sample = 0; break;
case 1: break;
case 3: sample = -sample; break;
2014-03-31 22:03:10 +00:00
}
2014-04-02 18:17:23 +00:00
phase++;
if(phase >= 4) phase = 0;
2014-03-31 22:03:10 +00:00
*buffer++ = sample;
2014-04-04 23:26:45 +00:00
sample_count++;
2014-03-31 22:03:10 +00:00
}
}
void set_rds_pi(uint16_t pi_code) {
2014-04-02 20:48:41 +00:00
rds_params.pi = pi_code;
}
void set_rds_rt(char *rt) {
strncpy(rds_params.rt, rt, 64);
2014-04-06 16:44:25 +00:00
for(int i=0; i<64; i++) {
if(rds_params.rt[i] == 0) rds_params.rt[i] = 32;
}
}
void set_rds_ps(char *ps) {
strncpy(rds_params.ps, ps, 8);
2014-04-06 16:44:25 +00:00
for(int i=0; i<8; i++) {
if(rds_params.ps[i] == 0) rds_params.ps[i] = 32;
}
}
void set_rds_ta(int ta) {
rds_params.ta = ta;
}