mirror of
https://github.com/radio95-rnt/rds95.git
synced 2026-02-27 04:43:52 +01:00
won't work (100%)
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -6,6 +6,7 @@
|
||||
"rds.h": "c",
|
||||
"lib.h": "c",
|
||||
"common.h": "c",
|
||||
"modulator.h": "c"
|
||||
"modulator.h": "c",
|
||||
"control_pipe.h": "c"
|
||||
}
|
||||
}
|
||||
177
src/ascii_cmd.c
177
src/ascii_cmd.c
@@ -13,23 +13,23 @@ typedef struct {
|
||||
} command_handler_t;
|
||||
|
||||
// Command handlers
|
||||
static void handle_ptyn(unsigned char *arg) {
|
||||
static void handle_ptyn(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[PTYN_LENGTH] = 0;
|
||||
set_rds_ptyn(xlat(arg));
|
||||
set_rds_ptyn(enc, xlat(arg));
|
||||
}
|
||||
|
||||
static void handle_afch(unsigned char *arg) {
|
||||
static void handle_afch(unsigned char *arg, RDSModulator* enc) {
|
||||
if (arg[0] == 'A' || arg[0] == 'B') {
|
||||
return;
|
||||
}
|
||||
if(arg[0] == '\0') {
|
||||
clear_rds_af();
|
||||
memset(enc->enc->data->af, 0, sizeof(enc->enc->data->af));
|
||||
return;
|
||||
}
|
||||
|
||||
clear_rds_af();
|
||||
memset(enc->enc->data->af, 0, sizeof(enc->enc->data->af));
|
||||
uint8_t arg_count;
|
||||
rds_af_t new_af;
|
||||
RDSAFs new_af;
|
||||
uint8_t af[MAX_AFS], *af_iter;
|
||||
|
||||
arg_count = sscanf((char *)arg,
|
||||
@@ -54,105 +54,111 @@ static void handle_afch(unsigned char *arg) {
|
||||
af_iter++;
|
||||
}
|
||||
|
||||
set_rds_af(new_af);
|
||||
memcpy(enc->enc->data->af, &new_af, sizeof(enc->enc->data->af));
|
||||
}
|
||||
|
||||
static void handle_tps(unsigned char *arg) {
|
||||
static void handle_tps(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[PS_LENGTH * 2] = 0;
|
||||
set_rds_tps(xlat(arg));
|
||||
set_rds_tps(enc, xlat(arg));
|
||||
}
|
||||
|
||||
static void handle_rt1(unsigned char *arg) {
|
||||
static void handle_rt1(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[RT_LENGTH * 2] = 0;
|
||||
set_rds_rt1(xlat(arg));
|
||||
set_rds_rt1(enc, xlat(arg));
|
||||
}
|
||||
|
||||
static void handle_pty(unsigned char *arg) {
|
||||
static void handle_pty(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[2] = 0;
|
||||
set_rds_pty(strtoul((char *)arg, NULL, 10));
|
||||
enc->enc->data->pty = strtoul((char *)arg, NULL, 10);
|
||||
}
|
||||
|
||||
static void handle_ecc(unsigned char *arg) {
|
||||
static void handle_ecc(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[2] = 0;
|
||||
set_rds_ecc(strtoul((char *)arg, NULL, 16));
|
||||
enc->enc->data->ecc = strtoul((char *)arg, NULL, 16);
|
||||
}
|
||||
|
||||
static void handle_lic(unsigned char *arg) {
|
||||
static void handle_lic(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[2] = 0;
|
||||
set_rds_lic(strtoul((char *)arg, NULL, 16));
|
||||
enc->enc->data->lic = strtoul((char *)arg, NULL, 16);
|
||||
}
|
||||
|
||||
static void handle_rtp(unsigned char *arg) {
|
||||
static void handle_rtp(unsigned char *arg, RDSModulator* enc) {
|
||||
char tag_names[2][32];
|
||||
uint8_t tags[6];
|
||||
|
||||
if (sscanf((char *)arg, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu",
|
||||
&tags[0], &tags[1], &tags[2], &tags[3], &tags[4], &tags[5]) == 6) {
|
||||
set_rds_rtplus_tags(tags);
|
||||
set_rds_rtplus_tags(enc, tags);
|
||||
} else if (sscanf((char *)arg, "%31[^,],%hhu,%hhu,%31[^,],%hhu,%hhu",
|
||||
tag_names[0], &tags[1], &tags[2], tag_names[1], &tags[4], &tags[5]) == 6) {
|
||||
tags[0] = get_rtp_tag_id(tag_names[0]);
|
||||
tags[3] = get_rtp_tag_id(tag_names[1]);
|
||||
set_rds_rtplus_tags(tags);
|
||||
set_rds_rtplus_tags(enc, tags);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_lps(unsigned char *arg) {
|
||||
static void handle_lps(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[LPS_LENGTH] = 0;
|
||||
set_rds_lps(arg);
|
||||
set_rds_lps(enc, arg);
|
||||
}
|
||||
|
||||
static void handle_pin(unsigned char *arg) {
|
||||
static void handle_pin(unsigned char *arg, RDSModulator* enc) {
|
||||
uint8_t pin[3];
|
||||
if (sscanf((char *)arg, "%hhu,%hhu,%hhu", &pin[0], &pin[1], &pin[2]) == 3) {
|
||||
set_rds_pin(pin[0], pin[1], pin[2]);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
enc->enc->data->pin[i+1] = pin[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_ps(unsigned char *arg) {
|
||||
static void handle_ps(unsigned char *arg, RDSModulator* enc) {
|
||||
if (arg[0] == '\0') arg[0] = ' '; // Fix for strings that start with a space
|
||||
arg[PS_LENGTH * 2] = 0;
|
||||
set_rds_ps(xlat(arg));
|
||||
set_rds_ps(enc, xlat(arg));
|
||||
}
|
||||
|
||||
static void handle_ct(unsigned char *arg) {
|
||||
set_rds_ct(arg[0]);
|
||||
static void handle_ct(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[2] = 1;
|
||||
enc->enc->data->ct = arg[0];
|
||||
}
|
||||
|
||||
static void handle_di(unsigned char *arg) {
|
||||
static void handle_di(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[2] = 0;
|
||||
set_rds_di(strtoul((char *)arg, NULL, 10));
|
||||
enc->enc->data->di = arg[0];
|
||||
}
|
||||
|
||||
static void handle_tp(unsigned char *arg) {
|
||||
set_rds_tp(arg[0]);
|
||||
static void handle_tp(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[1] = 0;
|
||||
enc->enc->data->tp = arg[0];
|
||||
}
|
||||
|
||||
static void handle_ta(unsigned char *arg) {
|
||||
set_rds_ta(arg[0]);
|
||||
static void handle_ta(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[1] = 0;
|
||||
enc->enc->data->ta = arg[0];
|
||||
}
|
||||
|
||||
static void handle_ms(unsigned char *arg) {
|
||||
set_rds_ms(arg[0]);
|
||||
static void handle_ms(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[1] = 0;
|
||||
enc->enc->data->ms = arg[0];
|
||||
}
|
||||
|
||||
static void handle_pi(unsigned char *arg) {
|
||||
static void handle_pi(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[4] = 0;
|
||||
set_rds_pi(strtoul((char *)arg, NULL, 16));
|
||||
enc->enc->data->pi = strtoul((char *)arg, NULL, 16);
|
||||
}
|
||||
|
||||
static void handle_af(unsigned char *arg) {
|
||||
static void handle_af(unsigned char *arg, RDSModulator* enc) {
|
||||
if (arg[0] == 'A' || arg[0] == 'B') {
|
||||
return;
|
||||
}
|
||||
if(arg[0] == '\0') {
|
||||
clear_rds_af();
|
||||
memset(enc->enc->data->af, 0, sizeof(enc->enc->data->af));
|
||||
return;
|
||||
}
|
||||
|
||||
clear_rds_af();
|
||||
memset(enc->enc->data->af, 0, sizeof(enc->enc->data->af));
|
||||
uint8_t arg_count;
|
||||
rds_af_t new_af;
|
||||
RDSAFs new_af;
|
||||
float af[MAX_AFS], *af_iter;
|
||||
|
||||
arg_count = sscanf((char *)arg,
|
||||
@@ -168,63 +174,70 @@ static void handle_af(unsigned char *arg) {
|
||||
&af[20], &af[21], &af[22], &af[23], &af[24]);
|
||||
|
||||
af_iter = af;
|
||||
memset(&new_af, 0, sizeof(struct rds_af_t));
|
||||
|
||||
memset(&new_af, 0, sizeof(RDSAFs));
|
||||
|
||||
while (arg_count-- != 0) {
|
||||
add_rds_af(&new_af, *af_iter++);
|
||||
}
|
||||
|
||||
set_rds_af(new_af);
|
||||
memcpy(enc->enc->data->af, &new_af, sizeof(enc->enc->data->af));
|
||||
}
|
||||
|
||||
static void handle_g(unsigned char *arg) {
|
||||
static void handle_g(unsigned char *arg, RDSModulator* enc) {
|
||||
uint16_t blocks[3];
|
||||
int count = sscanf((char *)arg, "%4hx%4hx%4hx", &blocks[0], &blocks[1], &blocks[2]);
|
||||
if (count == 3) {
|
||||
set_rds_cg(blocks);
|
||||
enc->enc->state->custom_group[0] = 1;
|
||||
enc->enc->state->custom_group[1] = blocks[0];
|
||||
enc->enc->state->custom_group[2] = blocks[1];
|
||||
enc->enc->state->custom_group[3] = blocks[2];
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pinen(unsigned char *arg) {
|
||||
static void handle_pinen(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[1] = 0;
|
||||
set_rds_pin_enabled(strtoul((char *)arg, NULL, 10));
|
||||
enc->enc->data->pin[0] = arg[0];
|
||||
}
|
||||
|
||||
static void handle_rt1en(unsigned char *arg) {
|
||||
set_rds_rt1_enabled(arg[0]);
|
||||
}
|
||||
|
||||
static void handle_ptynen(unsigned char *arg) {
|
||||
static void handle_rt1en(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[1] = 0;
|
||||
set_rds_ptyn_enabled(strtoul((char *)arg, NULL, 10));
|
||||
enc->enc->data->rt1_enabled = arg[0];
|
||||
}
|
||||
|
||||
static void handle_rtprun(unsigned char *arg) {
|
||||
static void handle_ptynen(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[1] = 0;
|
||||
set_rds_rtplus_flags(strtoul((char *)arg, NULL, 10));
|
||||
set_rds_ptyn_enabled(enc->enc, strtoul((char *)arg, NULL, 10));
|
||||
}
|
||||
|
||||
static void handle_eccen(unsigned char *arg) {
|
||||
set_rds_ecclic_toggle(arg[0]);
|
||||
static void handle_rtprun(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[1] = 0;
|
||||
set_rds_rtplus_flags(enc->enc, strtoul((char *)arg, NULL, 10));
|
||||
}
|
||||
|
||||
static void handle_shortrt(unsigned char *arg) {
|
||||
set_rds_shortrt(arg[0]);
|
||||
static void handle_eccen(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[1] = 0;
|
||||
enc->enc->data->ecclic_enabled = arg[0];
|
||||
}
|
||||
|
||||
static void handle_grpseq(unsigned char *arg) {
|
||||
set_rds_grpseq(arg);
|
||||
static void handle_shortrt(unsigned char *arg, RDSModulator* enc) {
|
||||
arg[1] = 0;
|
||||
enc->enc->data->shortrt = arg[0];
|
||||
}
|
||||
|
||||
static void handle_level(unsigned char *arg) {
|
||||
set_rds_level(strtoul((char *)arg, NULL, 10)/255.0f);
|
||||
static void handle_grpseq(unsigned char *arg, RDSModulator* enc) {
|
||||
memset(enc->enc->data->grp_sqc, 0, 24);
|
||||
memcpy(enc->enc->data->grp_sqc, arg, 24);
|
||||
}
|
||||
|
||||
static void handle_rdsgen(unsigned char *arg) {
|
||||
set_rds_gen(strtoul((char *)arg, NULL, 10));
|
||||
static void handle_level(unsigned char *arg, RDSModulator* enc) {
|
||||
enc->level = strtoul((char *)arg, NULL, 10)/255.0f;
|
||||
}
|
||||
|
||||
static void handle_udg1(unsigned char *arg) {
|
||||
static void handle_rdsgen(unsigned char *arg, RDSModulator* enc) {
|
||||
enc->rdsgen = strtoul((char *)arg, NULL, 10);
|
||||
}
|
||||
|
||||
static void handle_udg1(unsigned char *arg, RDSModulator* enc) {
|
||||
uint16_t blocks[8][3];
|
||||
int sets = 0;
|
||||
unsigned char *ptr = arg;
|
||||
@@ -250,9 +263,10 @@ static void handle_udg1(unsigned char *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
set_rds_udg1(sets, blocks);
|
||||
memcpy(enc->enc->data->udg2, &blocks, sets * sizeof(uint16_t[3]));
|
||||
enc->enc->data->udg2_len = sets;
|
||||
}
|
||||
static void handle_udg2(unsigned char *arg) {
|
||||
static void handle_udg2(unsigned char *arg, RDSModulator* enc) {
|
||||
uint16_t blocks[8][3]; // Up to 8 sets of 3 blocks each
|
||||
int sets = 0;
|
||||
unsigned char *ptr = arg;
|
||||
@@ -279,7 +293,8 @@ static void handle_udg2(unsigned char *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
set_rds_udg2(sets, blocks);
|
||||
memcpy(enc->enc->data->udg2, &blocks, sets * sizeof(uint16_t[3]));
|
||||
enc->enc->data->udg2_len = sets;
|
||||
}
|
||||
|
||||
// Command tables organized by delimiter position and command length
|
||||
@@ -337,17 +352,17 @@ static const command_handler_t commands_eq8[] = {
|
||||
|
||||
// Process a command using the appropriate command table
|
||||
static bool process_command_table(const command_handler_t *table, int table_size,
|
||||
unsigned char *cmd, unsigned char *arg) {
|
||||
unsigned char *cmd, unsigned char *arg, RDSModulator* enc) {
|
||||
for (int i = 0; i < table_size; i++) {
|
||||
if (ustrcmp(cmd, (unsigned char *)table[i].cmd) == 0) {
|
||||
table[i].handler(arg);
|
||||
table[i].handler(arg, enc);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void process_ascii_cmd(unsigned char *str) {
|
||||
void process_ascii_cmd(RDSModulator* enc, unsigned char *str) {
|
||||
unsigned char *cmd, *arg;
|
||||
uint16_t cmd_len = _strnlen((const char*)str, CTL_BUFFER_SIZE);
|
||||
|
||||
@@ -359,7 +374,7 @@ void process_ascii_cmd(unsigned char *str) {
|
||||
|
||||
if (process_command_table(commands_eq2,
|
||||
sizeof(commands_eq2) / sizeof(command_handler_t),
|
||||
cmd, arg)) {
|
||||
cmd, arg, enc)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -372,7 +387,7 @@ void process_ascii_cmd(unsigned char *str) {
|
||||
|
||||
if (process_command_table(commands_eq3,
|
||||
sizeof(commands_eq3) / sizeof(command_handler_t),
|
||||
cmd, arg)) {
|
||||
cmd, arg, enc)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -385,7 +400,7 @@ void process_ascii_cmd(unsigned char *str) {
|
||||
|
||||
if (process_command_table(commands_eq4,
|
||||
sizeof(commands_eq4) / sizeof(command_handler_t),
|
||||
cmd, arg)) {
|
||||
cmd, arg, enc)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -398,7 +413,7 @@ void process_ascii_cmd(unsigned char *str) {
|
||||
|
||||
if (process_command_table(commands_eq5,
|
||||
sizeof(commands_eq5) / sizeof(command_handler_t),
|
||||
cmd, arg)) {
|
||||
cmd, arg, enc)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -411,7 +426,7 @@ void process_ascii_cmd(unsigned char *str) {
|
||||
|
||||
if (process_command_table(commands_eq6,
|
||||
sizeof(commands_eq6) / sizeof(command_handler_t),
|
||||
cmd, arg)) {
|
||||
cmd, arg, enc)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -423,7 +438,7 @@ void process_ascii_cmd(unsigned char *str) {
|
||||
|
||||
if (process_command_table(commands_eq7,
|
||||
sizeof(commands_eq7) / sizeof(command_handler_t),
|
||||
cmd, arg)) {
|
||||
cmd, arg, enc)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -435,7 +450,7 @@ void process_ascii_cmd(unsigned char *str) {
|
||||
|
||||
if (process_command_table(commands_eq8,
|
||||
sizeof(commands_eq8) / sizeof(command_handler_t),
|
||||
cmd, arg)) {
|
||||
cmd, arg, enc)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
#define CTL_BUFFER_SIZE (CMD_BUFFER_SIZE * 2)
|
||||
#define READ_TIMEOUT_MS 100
|
||||
|
||||
extern void process_ascii_cmd(unsigned char *cmd);
|
||||
extern void process_ascii_cmd(RDSModulator* enc, unsigned char *str);
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "common.h"
|
||||
#include "ascii_cmd.h"
|
||||
#include "control_pipe.h"
|
||||
#include "rds.h"
|
||||
#include "modulator.h"
|
||||
|
||||
static int fd;
|
||||
static struct pollfd poller;
|
||||
@@ -22,7 +24,7 @@ int open_control_pipe(char *filename) {
|
||||
* Polls the control file (pipe), and if a command is received,
|
||||
* calls process_ascii_cmd.
|
||||
*/
|
||||
void poll_control_pipe() {
|
||||
void poll_control_pipe(RDSModulator* enc) {
|
||||
static unsigned char pipe_buf[CTL_BUFFER_SIZE];
|
||||
static unsigned char cmd_buf[CMD_BUFFER_SIZE];
|
||||
int bytes_read;
|
||||
@@ -45,7 +47,7 @@ void poll_control_pipe() {
|
||||
if (cmd_len > 0 && cmd_len < CMD_BUFFER_SIZE) {
|
||||
memset(cmd_buf, 0, CMD_BUFFER_SIZE);
|
||||
strncpy((char *)cmd_buf, token, CMD_BUFFER_SIZE - 1);
|
||||
process_ascii_cmd(cmd_buf);
|
||||
process_ascii_cmd(enc, cmd_buf);
|
||||
}
|
||||
token = strtok(NULL, "\n");
|
||||
}
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
|
||||
extern int open_control_pipe(char *filename);
|
||||
extern void close_control_pipe();
|
||||
extern void poll_control_pipe();
|
||||
extern void poll_control_pipe(RDSModulator* enc);
|
||||
|
||||
37
src/lib.c
37
src/lib.c
@@ -170,7 +170,7 @@ void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
||||
/*
|
||||
* AF stuff
|
||||
*/
|
||||
uint8_t add_rds_af(struct rds_af_t *af_list, float freq) {
|
||||
uint8_t add_rds_af(RDSAFs *af_list, float freq) {
|
||||
uint16_t af;
|
||||
uint8_t entries_reqd = 1; /* default for FM */
|
||||
|
||||
@@ -214,41 +214,6 @@ uint8_t add_rds_af(struct rds_af_t *af_list, float freq) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *show_af_list(struct rds_af_t af_list) {
|
||||
float freq;
|
||||
bool is_lfmf = false;
|
||||
static char outstr[255];
|
||||
uint8_t outstrlen = 0;
|
||||
|
||||
outstrlen += sprintf(outstr, "AF: %u,", af_list.num_afs);
|
||||
|
||||
for (uint8_t i = 0; i < af_list.num_entries; i++) {
|
||||
if (af_list.afs[i] == AF_CODE_LFMF_FOLLOWS) {
|
||||
/* The next AF will be for LF/MF */
|
||||
is_lfmf = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_lfmf) {
|
||||
if (af_list.afs[i] >= 1 && af_list.afs[i] <= 15) { /* LF */
|
||||
freq = 153.0f + ((float)(af_list.afs[i] - 1) * 9.0f);
|
||||
outstrlen += sprintf(outstr + outstrlen, " (LF)%.0f", freq);
|
||||
} else { /*if (af_list.afs[i] >= 16 && af_list.afs[i] <= 135) {*/ /* MF */
|
||||
freq = 531.0f + ((float)(af_list.afs[i] - 16) * 9.0f);
|
||||
outstrlen += sprintf(outstr + outstrlen, " (MF)%.0f", freq);
|
||||
}
|
||||
is_lfmf = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* FM */
|
||||
freq = (float)((uint16_t)af_list.afs[i] + 875) / 10.0f;
|
||||
outstrlen += sprintf(outstr + outstrlen, " %.1f", freq);
|
||||
}
|
||||
|
||||
return outstr;
|
||||
}
|
||||
|
||||
/*
|
||||
* UTF-8 to RDS char set converter
|
||||
*
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
#include "common.h"
|
||||
#include "rds.h"
|
||||
#include "waveforms.h"
|
||||
#include "modulator.h"
|
||||
|
||||
static struct rds_t rds;
|
||||
static float waveform[2][FILTER_SIZE];
|
||||
|
||||
static float level;
|
||||
static uint8_t rdsgen;
|
||||
void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc) {
|
||||
memset(rdsMod, 0, sizeof(*rdsMod));
|
||||
rdsMod->level = 1.0f;
|
||||
rdsMod->rdsgen = 1;
|
||||
|
||||
void init_rds_objects() {
|
||||
level = 1.0f;
|
||||
rdsgen = 1;
|
||||
|
||||
memset(&rds, 0, sizeof(rds));
|
||||
rdsMod->enc = enc;
|
||||
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
// This is the bpsk, so waveform[0] is 0 degrees out of phase but waveform[1] is 180 degrees, bpsk
|
||||
for (uint16_t j = 0; j < FILTER_SIZE; j++) {
|
||||
waveform[i][j] = i ?
|
||||
+waveform_biphase[j] : -waveform_biphase[j];
|
||||
@@ -24,53 +17,39 @@ void init_rds_objects() {
|
||||
}
|
||||
}
|
||||
|
||||
void set_rds_level(float _level) {
|
||||
level = fminf(1.0f, fmaxf(0.0f, _level));
|
||||
}
|
||||
void set_rds_gen(uint8_t _rdsgen) {
|
||||
rdsgen = _rdsgen & 1;
|
||||
}
|
||||
|
||||
/* Get an RDS sample. This generates the envelope of the waveform using
|
||||
* pre-generated elementary waveform samples.
|
||||
*/
|
||||
float get_rds_sample() {
|
||||
float get_rds_sample(RDSModulator* rdsMod) {
|
||||
uint16_t idx;
|
||||
float *cur_waveform;
|
||||
float sample;
|
||||
if (rds.sample_count == SAMPLES_PER_BIT) {
|
||||
// New Sample
|
||||
if (rds.bit_pos == BITS_PER_GROUP) {
|
||||
// New bit stream
|
||||
get_rds_bits(rds.bit_buffer);
|
||||
if (rdsMod->sample_count == SAMPLES_PER_BIT) {
|
||||
if (rdsMod->bit_pos == BITS_PER_GROUP) {
|
||||
get_rds_bits(rdsMod->enc, rdsMod->bit_buffer);
|
||||
rds.bit_pos = 0;
|
||||
}
|
||||
|
||||
// Differentially encode, so 1111 becomes 1000 and 0001 becomes 0001
|
||||
rds.cur_bit = rds.bit_buffer[rds.bit_pos++];
|
||||
rds.prev_output = rds.cur_output;
|
||||
rds.cur_output = rds.prev_output ^ rds.cur_bit;
|
||||
rdsMod->cur_bit = rdsMod->bit_buffer[rdsMod->bit_pos++];
|
||||
rdsMod->prev_output = rdsMod->cur_output;
|
||||
rdsMod->cur_output = rdsMod->prev_output ^ rdsMod->cur_bit;
|
||||
|
||||
idx = rds.in_sample_index;
|
||||
cur_waveform = waveform[rds.cur_output]; // get the waveform, this is the biphase in a 0/180 degree phase shift
|
||||
idx = rdsMod->in_sample_index;
|
||||
cur_waveform = waveform[rdsMod->cur_output];
|
||||
|
||||
for (uint16_t i = 0; i < FILTER_SIZE; i++) {
|
||||
rds.sample_buffer[idx++] += *cur_waveform++;
|
||||
rdsMod->sample_buffer[idx++] += *cur_waveform++;
|
||||
if (idx == SAMPLE_BUFFER_SIZE) idx = 0;
|
||||
}
|
||||
|
||||
rds.in_sample_index += SAMPLES_PER_BIT;
|
||||
if (rds.in_sample_index == SAMPLE_BUFFER_SIZE)
|
||||
rds.in_sample_index = 0;
|
||||
rdsMod->in_sample_index += SAMPLES_PER_BIT;
|
||||
if (rdsMod->in_sample_index == SAMPLE_BUFFER_SIZE) rdsMod->in_sample_index = 0;
|
||||
|
||||
rds.sample_count = 0;
|
||||
rdsMod->sample_count = 0;
|
||||
}
|
||||
rds.sample_count++;
|
||||
rdsMod->sample_count++;
|
||||
|
||||
sample = rds.sample_buffer[rds.out_sample_index];
|
||||
sample = rdsMod->sample_buffer[rdsMod->out_sample_index];
|
||||
|
||||
rds.sample_buffer[rds.out_sample_index++] = 0;
|
||||
if (rds.out_sample_index == SAMPLE_BUFFER_SIZE)
|
||||
rds.out_sample_index = 0;
|
||||
return sample*level*rdsgen;
|
||||
rdsMod->sample_buffer[rdsMod->out_sample_index++] = 0;
|
||||
if (rdsMod->out_sample_index == SAMPLE_BUFFER_SIZE)
|
||||
rdsMod->out_sample_index = 0;
|
||||
return sample*rdsMod->level*rdsMod->rdsgen;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/* RDS signal context */
|
||||
typedef struct rds_t {
|
||||
#include "common.h"
|
||||
#include "rds.h"
|
||||
#include "waveforms.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t bit_buffer[BITS_PER_GROUP];
|
||||
uint8_t bit_pos;
|
||||
float sample_buffer[SAMPLE_BUFFER_SIZE];
|
||||
@@ -9,8 +12,10 @@ typedef struct rds_t {
|
||||
uint8_t sample_count;
|
||||
uint16_t in_sample_index;
|
||||
uint16_t out_sample_index;
|
||||
} rds_t;
|
||||
float level;
|
||||
uint8_t rdsgen;
|
||||
RDSEncoder* enc;
|
||||
} RDSModulator;
|
||||
|
||||
extern void init_rds_objects();
|
||||
extern void set_rds_level(float _level);
|
||||
extern void set_rds_gen(uint8_t _rdsgen);
|
||||
void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc);
|
||||
float get_rds_sample(RDSModulator* rdsMod);
|
||||
|
||||
583
src/rds.c
583
src/rds.c
@@ -4,81 +4,62 @@
|
||||
#include "lib.h"
|
||||
#include <time.h>
|
||||
|
||||
static struct rds_params_t rds_data;
|
||||
|
||||
// #region Struct Defs
|
||||
static struct {
|
||||
uint8_t ecclic_enabled;
|
||||
uint8_t ecc_or_lic;
|
||||
|
||||
uint8_t ps_update;
|
||||
uint8_t tps_update;
|
||||
|
||||
uint8_t rt_update;
|
||||
uint8_t rt_ab;
|
||||
uint8_t rt_segments;
|
||||
|
||||
uint8_t ptyn_enabled;
|
||||
uint8_t ptyn_update;
|
||||
uint8_t ptyn_ab;
|
||||
|
||||
uint8_t lps_update;
|
||||
uint8_t lps_segments;
|
||||
|
||||
uint16_t custom_group[GROUP_LENGTH];
|
||||
|
||||
uint8_t rtp_oda;
|
||||
uint8_t grp_seq_idx[2];
|
||||
uint8_t udg_idxs[2];
|
||||
} rds_state;
|
||||
|
||||
// #region ODA
|
||||
static struct rds_oda_t odas[MAX_ODAS];
|
||||
static struct {
|
||||
uint8_t current;
|
||||
uint8_t count;
|
||||
} oda_state;
|
||||
|
||||
static struct {
|
||||
uint8_t group;
|
||||
uint8_t enabled;
|
||||
uint8_t running;
|
||||
uint8_t toggle;
|
||||
uint8_t type[2];
|
||||
uint8_t start[2];
|
||||
uint8_t len[2];
|
||||
} rtplus_cfg;
|
||||
// #endregion
|
||||
// #endregion
|
||||
|
||||
// #region Helper funcs
|
||||
static void register_oda(uint8_t group, uint16_t aid, uint16_t scb) {
|
||||
if (oda_state.count >= MAX_ODAS) return;
|
||||
|
||||
odas[oda_state.count].group = group;
|
||||
odas[oda_state.count].aid = aid;
|
||||
odas[oda_state.count].scb = scb;
|
||||
oda_state.count++;
|
||||
void saveToFile(const char *filename, RDSEncoder *emp) {
|
||||
FILE *file = fopen(filename, "wb");
|
||||
if (file == NULL) {
|
||||
perror("Error opening file");
|
||||
return;
|
||||
}
|
||||
fwrite(emp, sizeof(RDSEncoder), 1, file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
static uint16_t get_next_af() {
|
||||
void loadFromFile(const char *filename, RDSEncoder *emp) {
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (file == NULL) {
|
||||
perror("Error opening file");
|
||||
return;
|
||||
}
|
||||
fread(emp, sizeof(RDSEncoder), 1, file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
int fileExists(const char *filename) {
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (file) {
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_oda(RDSEncoder* enc, uint8_t group, uint16_t aid, uint16_t scb) {
|
||||
if (enc->oda_state[enc->program].count >= MAX_ODAS) return;
|
||||
|
||||
enc->odas[enc->program][oda_state.count].group = group;
|
||||
enc->odas[enc->program][oda_state.count].aid = aid;
|
||||
enc->odas[enc->program][oda_state.count].scb = scb;
|
||||
enc->oda_state[enc->program].count++;
|
||||
}
|
||||
|
||||
static uint16_t get_next_af(RDSEncoder* enc) {
|
||||
static uint8_t af_state;
|
||||
uint16_t out;
|
||||
|
||||
if (rds_data.af.num_afs) {
|
||||
if (enc->data[enc->program].af.num_afs) {
|
||||
if (af_state == 0) {
|
||||
out = (AF_CODE_NUM_AFS_BASE + rds_data.af.num_afs) << 8;
|
||||
out |= rds_data.af.afs[0];
|
||||
out = (AF_CODE_NUM_AFS_BASE + enc->data[enc->program].af.num_afs) << 8;
|
||||
out |= enc->data[enc->program].af.afs[0];
|
||||
af_state += 1;
|
||||
} else {
|
||||
out = rds_data.af.afs[af_state] << 8;
|
||||
if (rds_data.af.afs[af_state + 1])
|
||||
out |= rds_data.af.afs[af_state + 1];
|
||||
out = enc->data[enc->program].af.afs[af_state] << 8;
|
||||
if (enc->data[enc->program].af.afs[af_state + 1])
|
||||
out |= enc->data[enc->program].af.afs[af_state + 1];
|
||||
else
|
||||
out |= AF_CODE_FILLER;
|
||||
af_state += 2;
|
||||
}
|
||||
if (af_state >= rds_data.af.num_entries) af_state = 0;
|
||||
if (af_state >= enc->data[enc->program].af.num_entries) af_state = 0;
|
||||
} else {
|
||||
out = AF_CODE_NO_AF << 8 | AF_CODE_FILLER;
|
||||
}
|
||||
@@ -88,26 +69,26 @@ static uint16_t get_next_af() {
|
||||
// #endregion
|
||||
|
||||
// #region Group encoding
|
||||
static void get_rds_ps_group(uint16_t *blocks) {
|
||||
static void get_rds_ps_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
static unsigned char ps_text[PS_LENGTH];
|
||||
static unsigned char tps_text[PS_LENGTH];
|
||||
static uint8_t ps_csegment;
|
||||
|
||||
if (ps_csegment == 0 && rds_state.ps_update) {
|
||||
memcpy(ps_text, rds_data.ps, PS_LENGTH);
|
||||
rds_state.ps_update = 0;
|
||||
if (ps_csegment == 0 && enc->state[enc->program][enc->program].ps_update) {
|
||||
memcpy(ps_text, enc->data[enc->program].ps, PS_LENGTH);
|
||||
enc->state[enc->program][enc->program].ps_update = 0;
|
||||
}
|
||||
if(ps_csegment == 0 && rds_state.tps_update) {
|
||||
memcpy(tps_text, rds_data.tps, PS_LENGTH);
|
||||
rds_state.tps_update = 0;
|
||||
if(ps_csegment == 0 && enc->state[enc->program][enc->program].tps_update) {
|
||||
memcpy(tps_text, enc->data[enc->program].tps, PS_LENGTH);
|
||||
enc->state[enc->program][enc->program].tps_update = 0;
|
||||
}
|
||||
|
||||
blocks[1] |= rds_data.ta << 4;
|
||||
blocks[1] |= rds_data.ms << 3;
|
||||
blocks[1] |= ((rds_data.di >> (3 - ps_csegment)) & INT8_0) << 2;
|
||||
blocks[1] |= enc->data[enc->program].ta << 4;
|
||||
blocks[1] |= enc->data[enc->program].ms << 3;
|
||||
blocks[1] |= ((enc->data[enc->program].di >> (3 - ps_csegment)) & INT8_0) << 2;
|
||||
blocks[1] |= ps_csegment;
|
||||
blocks[2] = get_next_af();
|
||||
if(rds_data.ta && tps_text[0] != '\0') {
|
||||
blocks[2] = get_next_af(enc);
|
||||
if(enc->data[enc->program].ta && tps_text[0] != '\0') {
|
||||
blocks[3] = tps_text[ps_csegment * 2] << 8 | tps_text[ps_csegment * 2 + 1];
|
||||
} else {
|
||||
/* TODO: Add DPS */
|
||||
@@ -117,19 +98,19 @@ static void get_rds_ps_group(uint16_t *blocks) {
|
||||
if (ps_csegment >= 4) ps_csegment = 0;
|
||||
}
|
||||
|
||||
static uint8_t get_rds_rt_group(uint16_t *blocks) {
|
||||
static uint8_t get_rds_rt_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
static unsigned char rt_text[RT_LENGTH];
|
||||
static uint8_t rt_state;
|
||||
|
||||
if (rds_state.rt_update) {
|
||||
memcpy(rt_text, rds_data.rt1, RT_LENGTH);
|
||||
rds_state.rt_ab ^= 1;
|
||||
rds_state.rt_update = 0;
|
||||
if (enc->state[enc->program].rt_update) {
|
||||
memcpy(rt_text, enc->data[enc->program].rt1, RT_LENGTH);
|
||||
enc->state[enc->program].rt_ab ^= 1;
|
||||
enc->state[enc->program].rt_update = 0;
|
||||
rt_state = 0;
|
||||
}
|
||||
|
||||
blocks[1] |= 2 << 12;
|
||||
blocks[1] |= rds_state.rt_ab << 4;
|
||||
blocks[1] |= enc->state[enc->program].rt_ab << 4;
|
||||
blocks[1] |= rt_state;
|
||||
blocks[2] = rt_text[rt_state * 4 ] << 8;
|
||||
blocks[2] |= rt_text[rt_state * 4 + 1];
|
||||
@@ -137,12 +118,12 @@ static uint8_t get_rds_rt_group(uint16_t *blocks) {
|
||||
blocks[3] |= rt_text[rt_state * 4 + 3];
|
||||
|
||||
rt_state++;
|
||||
if (rt_state >= rds_state.rt_segments) rt_state = 0;
|
||||
if (rt_state >= enc->state[enc->program].rt_segments) rt_state = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void get_rds_oda_group(uint16_t *blocks) {
|
||||
struct rds_oda_t this_oda = odas[oda_state.current];
|
||||
static void get_rds_oda_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
RDSODA this_oda = enc->odas[enc->oda_state[enc->program].current];
|
||||
|
||||
blocks[1] |= 3 << 12;
|
||||
|
||||
@@ -151,11 +132,11 @@ static void get_rds_oda_group(uint16_t *blocks) {
|
||||
blocks[2] = this_oda.scb;
|
||||
blocks[3] = this_oda.aid;
|
||||
|
||||
oda_state.current++;
|
||||
if (oda_state.current >= oda_state.count) oda_state.current = 0;
|
||||
enc->oda_state[enc->program].current++;
|
||||
if (enc->oda_state[enc->program].current >= enc->oda_state[enc->program].count) enc->oda_state[enc->program].current = 0;
|
||||
}
|
||||
|
||||
static uint8_t get_rds_ct_group(uint16_t *blocks) {
|
||||
static uint8_t get_rds_ct_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
static uint8_t latest_minutes;
|
||||
struct tm *utc, *local_time;
|
||||
time_t now;
|
||||
@@ -190,18 +171,18 @@ static uint8_t get_rds_ct_group(uint16_t *blocks) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_rds_ptyn_group(uint16_t *blocks) {
|
||||
static void get_rds_ptyn_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
static unsigned char ptyn_text[PTYN_LENGTH];
|
||||
static uint8_t ptyn_state;
|
||||
|
||||
if (ptyn_state == 0 && rds_state.ptyn_update) {
|
||||
memcpy(ptyn_text, rds_data.ptyn, PTYN_LENGTH);
|
||||
rds_state.ptyn_ab ^= 1;
|
||||
rds_state.ptyn_update = 0;
|
||||
if (ptyn_state == 0 && enc->state[enc->program].ptyn_update) {
|
||||
memcpy(ptyn_text, enc->data[enc->program].ptyn, PTYN_LENGTH);
|
||||
enc->state[enc->program].ptyn_ab ^= 1;
|
||||
enc->state[enc->program].ptyn_update = 0;
|
||||
}
|
||||
|
||||
blocks[1] |= 10 << 12 | ptyn_state;
|
||||
blocks[1] |= rds_state.ptyn_ab << 4;
|
||||
blocks[1] |= enc->state[enc->program].ptyn_ab << 4;
|
||||
blocks[2] = ptyn_text[ptyn_state * 4 + 0] << 8;
|
||||
blocks[2] |= ptyn_text[ptyn_state * 4 + 1];
|
||||
blocks[3] = ptyn_text[ptyn_state * 4 + 2] << 8;
|
||||
@@ -211,13 +192,13 @@ static void get_rds_ptyn_group(uint16_t *blocks) {
|
||||
if (ptyn_state == 2) ptyn_state = 0;
|
||||
}
|
||||
|
||||
static void get_rds_lps_group(uint16_t *blocks) {
|
||||
static void get_rds_lps_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
static unsigned char lps_text[LPS_LENGTH];
|
||||
static uint8_t lps_state;
|
||||
|
||||
if (lps_state == 0 && rds_state.lps_update) {
|
||||
memcpy(lps_text, rds_data.lps, LPS_LENGTH);
|
||||
rds_state.lps_update = 0;
|
||||
if (lps_state == 0 && enc->state[enc->program].lps_update) {
|
||||
memcpy(lps_text, enc->data[enc->program].lps, LPS_LENGTH);
|
||||
enc->state[enc->program].lps_update = 0;
|
||||
}
|
||||
|
||||
blocks[1] |= 15 << 12 | lps_state;
|
||||
@@ -227,72 +208,72 @@ static void get_rds_lps_group(uint16_t *blocks) {
|
||||
blocks[3] |= lps_text[lps_state * 4 + 3];
|
||||
|
||||
lps_state++;
|
||||
if (lps_state == rds_state.lps_segments) lps_state = 0;
|
||||
if (lps_state == enc->state[enc->program].lps_segments) lps_state = 0;
|
||||
}
|
||||
|
||||
static void get_rds_ecc_group(uint16_t *blocks) {
|
||||
static void get_rds_ecc_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
blocks[1] |= 1 << 12;
|
||||
blocks[2] = rds_data.ecc;
|
||||
blocks[2] = enc->data[enc->program].ecc;
|
||||
|
||||
if(rds_data.pin[0]) {
|
||||
blocks[3] = rds_data.pin[1] << 11; // day
|
||||
blocks[3] |= rds_data.pin[2] << 6; // hour
|
||||
blocks[3] |= rds_data.pin[3]; // minute
|
||||
if(enc->data[enc->program].pin[0]) {
|
||||
blocks[3] = enc->data[enc->program].pin[1] << 11; // day
|
||||
blocks[3] |= enc->data[enc->program].pin[2] << 6; // hour
|
||||
blocks[3] |= enc->data[enc->program].pin[3]; // minute
|
||||
}
|
||||
}
|
||||
|
||||
static void get_rds_lic_group(uint16_t *blocks) {
|
||||
static void get_rds_lic_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
blocks[1] |= 1 << 12;
|
||||
blocks[2] = 0x3000; // 0b0011000000000000
|
||||
blocks[2] |= rds_data.lic;
|
||||
blocks[2] |= enc->data[enc->program].lic;
|
||||
|
||||
if(rds_data.pin[0]) {
|
||||
blocks[3] = rds_data.pin[1] << 11; // day
|
||||
blocks[3] |= rds_data.pin[2] << 6; // hour
|
||||
blocks[3] |= rds_data.pin[3]; // minute
|
||||
if(enc->data[enc->program].pin[0]) {
|
||||
blocks[3] = enc->data[enc->program].pin[1] << 11; // day
|
||||
blocks[3] |= enc->data[enc->program].pin[2] << 6; // hour
|
||||
blocks[3] |= enc->data[enc->program].pin[3]; // minute
|
||||
}
|
||||
}
|
||||
static void get_rds_rtplus_group(uint16_t *blocks) {
|
||||
blocks[1] |= GET_GROUP_TYPE(rtplus_cfg.group) << 12;
|
||||
blocks[1] |= GET_GROUP_VER(rtplus_cfg.group) << 11;
|
||||
blocks[1] |= rtplus_cfg.toggle << 4 | rtplus_cfg.running << 3;
|
||||
blocks[1] |= (rtplus_cfg.type[0] & INT8_U5) >> 3;
|
||||
static void get_rds_rtplus_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
blocks[1] |= GET_GROUP_TYPE(enc->rtpData[enc->program].group) << 12;
|
||||
blocks[1] |= GET_GROUP_VER(enc->rtpData[enc->program].group) << 11;
|
||||
blocks[1] |= enc->rtpData[enc->program].toggle << 4 | enc->rtpData[enc->program].running << 3;
|
||||
blocks[1] |= (enc->rtpData[enc->program].type[0] & INT8_U5) >> 3;
|
||||
|
||||
blocks[2] = (rtplus_cfg.type[0] & INT8_L3) << 13;
|
||||
blocks[2] |= (rtplus_cfg.start[0] & INT8_L6) << 7;
|
||||
blocks[2] |= (rtplus_cfg.len[0] & INT8_L6) << 1;
|
||||
blocks[2] |= (rtplus_cfg.type[1] & INT8_U3) >> 5;
|
||||
blocks[2] = (enc->rtpData[enc->program].type[0] & INT8_L3) << 13;
|
||||
blocks[2] |= (enc->rtpData[enc->program].start[0] & INT8_L6) << 7;
|
||||
blocks[2] |= (enc->rtpData[enc->program].len[0] & INT8_L6) << 1;
|
||||
blocks[2] |= (enc->rtpData[enc->program].type[1] & INT8_U3) >> 5;
|
||||
|
||||
blocks[3] = (rtplus_cfg.type[1] & INT8_L5) << 11;
|
||||
blocks[3] |= (rtplus_cfg.start[1] & INT8_L6) << 5;
|
||||
blocks[3] |= rtplus_cfg.len[1] & INT8_L5;
|
||||
blocks[3] = (enc->rtpData[enc->program].type[1] & INT8_L5) << 11;
|
||||
blocks[3] |= (enc->rtpData[enc->program].start[1] & INT8_L6) << 5;
|
||||
blocks[3] |= enc->rtpData[enc->program].len[1] & INT8_L5;
|
||||
}
|
||||
// #endregion
|
||||
|
||||
static uint8_t get_rds_custom_groups(uint16_t *blocks) {
|
||||
if(rds_state.custom_group[0] == 1) {
|
||||
rds_state.custom_group[0] = 0;
|
||||
blocks[0] = rds_data.pi;
|
||||
blocks[1] = rds_state.custom_group[1];
|
||||
blocks[2] = rds_state.custom_group[2];
|
||||
blocks[3] = rds_state.custom_group[3];
|
||||
static uint8_t get_rds_custom_groups(RDSEncoder* enc, uint16_t *blocks) {
|
||||
if(enc->state[enc->program].custom_group[0] == 1) {
|
||||
enc->state[enc->program].custom_group[0] = 0;
|
||||
blocks[0] = enc->data[enc->program].pi;
|
||||
blocks[1] = enc->state[enc->program].custom_group[1];
|
||||
blocks[2] = enc->state[enc->program].custom_group[2];
|
||||
blocks[3] = enc->state[enc->program].custom_group[3];
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_rds_group(uint16_t *blocks) {
|
||||
blocks[0] = rds_data.pi;
|
||||
blocks[1] = rds_data.tp << 10;
|
||||
blocks[1] |= rds_data.pty << 5;
|
||||
static void get_rds_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
blocks[0] = enc->data[enc->program].pi;
|
||||
blocks[1] = enc->data[enc->program].tp << 10;
|
||||
blocks[1] |= enc->data[enc->program].pty << 5;
|
||||
blocks[2] = 0;
|
||||
blocks[3] = 0;
|
||||
|
||||
if (rds_data.ct && get_rds_ct_group(blocks)) {
|
||||
if (enc->data[enc->program].ct && get_rds_ct_group(enc, blocks)) {
|
||||
goto group_coded;
|
||||
}
|
||||
|
||||
if(get_rds_custom_groups(blocks)) {
|
||||
if(get_rds_custom_groups(enc, blocks)) {
|
||||
goto group_coded;
|
||||
}
|
||||
|
||||
@@ -301,22 +282,22 @@ static void get_rds_group(uint16_t *blocks) {
|
||||
char grp;
|
||||
|
||||
while(good_group == 0) {
|
||||
uint8_t grp_sqc_idx = rds_state.grp_seq_idx[0]++;
|
||||
if(rds_data.grp_sqc[grp_sqc_idx] == '\0') {
|
||||
rds_state.grp_seq_idx[0] = 0;
|
||||
uint8_t grp_sqc_idx = enc->state[enc->program].grp_seq_idx[0]++;
|
||||
if(enc->data[enc->program].grp_sqc[grp_sqc_idx] == '\0') {
|
||||
enc->state[enc->program].grp_seq_idx[0] = 0;
|
||||
grp_sqc_idx = 0;
|
||||
}
|
||||
grp = rds_data.grp_sqc[grp_sqc_idx];
|
||||
grp = enc->data[enc->program].grp_sqc[grp_sqc_idx];
|
||||
|
||||
if(grp == '0') good_group = 1;
|
||||
if(grp == '1' && rds_state.ecclic_enabled) good_group = 1;
|
||||
if(grp == '2' && rds_data.rt1_enabled) good_group = 1;
|
||||
if(grp == 'A' && rds_state.ptyn_enabled) good_group = 1;
|
||||
if(grp == 'X' && rds_data.udg1_len != 0) good_group = 1;
|
||||
if(grp == 'Y' && rds_data.udg2_len != 0) good_group = 1;
|
||||
if(grp == 'R' && rtplus_cfg.enabled) good_group = 1;
|
||||
if(grp == '3' && oda_state.count != 0) good_group = 1;
|
||||
if(grp == 'F' && rds_data.lps[0] != '\0') good_group = 1;
|
||||
if(grp == '1' && enc->data[enc->program].ecclic_enabled) good_group = 1;
|
||||
if(grp == '2' && enc->data[enc->program].rt1_enabled) good_group = 1;
|
||||
if(grp == 'A' && enc->state[enc->program].ptyn_enabled) good_group = 1;
|
||||
if(grp == 'X' && enc->data[enc->program].udg1_len != 0) good_group = 1;
|
||||
if(grp == 'Y' && enc->data[enc->program].udg2_len != 0) good_group = 1;
|
||||
if(grp == 'R' && enc->rtpData[enc->program].enabled) good_group = 1;
|
||||
if(grp == '3' && enc->oda_state[enc->program].count != 0) good_group = 1;
|
||||
if(grp == 'F' && enc->data[enc->program].lps[0] != '\0') good_group = 1;
|
||||
|
||||
if(!good_group) cant_find_group++;
|
||||
else cant_find_group = 0;
|
||||
@@ -332,311 +313,201 @@ static void get_rds_group(uint16_t *blocks) {
|
||||
{
|
||||
default:
|
||||
case '0':
|
||||
if(rds_state.grp_seq_idx[1] != 3) rds_state.grp_seq_idx[0]--;
|
||||
else rds_state.grp_seq_idx[1] = 0;
|
||||
rds_state.grp_seq_idx[1]++;
|
||||
get_rds_ps_group(blocks);
|
||||
if(enc->state[enc->program].grp_seq_idx[1] != 3) enc->state[enc->program].grp_seq_idx[0]--;
|
||||
else enc->state[enc->program].grp_seq_idx[1] = 0;
|
||||
enc->state[enc->program].grp_seq_idx[1]++;
|
||||
get_rds_ps_group(enc, blocks);
|
||||
goto group_coded;
|
||||
case '1':
|
||||
if(rds_data.ecc && rds_data.lic) {
|
||||
if(rds_state.ecc_or_lic == 0) {
|
||||
get_rds_ecc_group(blocks);
|
||||
if(enc->data[enc->program].ecc && enc->data[enc->program].lic) {
|
||||
if(enc->state[enc->program].ecc_or_lic == 0) {
|
||||
get_rds_ecc_group(enc, blocks);
|
||||
} else {
|
||||
get_rds_lic_group(blocks);
|
||||
get_rds_lic_group(enc, blocks);
|
||||
}
|
||||
rds_state.ecc_or_lic ^= 1;
|
||||
} else if(rds_data.lic) {
|
||||
get_rds_lic_group(blocks);
|
||||
enc->state[enc->program].ecc_or_lic ^= 1;
|
||||
} else if(enc->data[enc->program].lic) {
|
||||
get_rds_lic_group(enc, blocks);
|
||||
} else {
|
||||
get_rds_ecc_group(blocks);
|
||||
get_rds_ecc_group(enc, blocks);
|
||||
}
|
||||
goto group_coded;
|
||||
case '2':
|
||||
get_rds_rt_group(blocks);
|
||||
get_rds_rt_group(enc, blocks);
|
||||
goto group_coded;
|
||||
case 'A':
|
||||
get_rds_ptyn_group(blocks);
|
||||
get_rds_ptyn_group(enc, blocks);
|
||||
goto group_coded;
|
||||
// TODO: Add EON
|
||||
case 'X':
|
||||
idx = rds_state.udg_idxs[0];
|
||||
for(int i = 0; i < 3; i++) blocks[i+1] = rds_data.udg1[idx][i];
|
||||
rds_state.udg_idxs[0]++;
|
||||
if(rds_state.udg_idxs[0] == rds_data.udg1_len) rds_state.udg_idxs[0] = 0;
|
||||
idx = enc->state[enc->program].udg_idxs[0];
|
||||
for(int i = 0; i < 3; i++) blocks[i+1] = enc->data[enc->program].udg1[idx][i];
|
||||
enc->state[enc->program].udg_idxs[0]++;
|
||||
if(enc->state[enc->program].udg_idxs[0] == enc->data[enc->program].udg1_len) enc->state[enc->program].udg_idxs[0] = 0;
|
||||
goto group_coded;
|
||||
case 'Y':
|
||||
idx = rds_state.udg_idxs[1];
|
||||
for(int i = 0; i < 3; i++) blocks[i+1] = rds_data.udg2[idx][i];
|
||||
rds_state.udg_idxs[1]++;
|
||||
if(rds_state.udg_idxs[1] == rds_data.udg2_len) rds_state.udg_idxs[1] = 0;
|
||||
idx = enc->state[enc->program].udg_idxs[1];
|
||||
for(int i = 0; i < 3; i++) blocks[i+1] = enc->data[enc->program].udg2[idx][i];
|
||||
enc->state[enc->program].udg_idxs[1]++;
|
||||
if(enc->state[enc->program].udg_idxs[1] == enc->data[enc->program].udg2_len) enc->state[enc->program].udg_idxs[1] = 0;
|
||||
goto group_coded;
|
||||
case 'R':
|
||||
if(rds_state.rtp_oda == 0) {
|
||||
get_rds_rtplus_group(blocks);
|
||||
if(enc->state[enc->program].rtp_oda == 0) {
|
||||
get_rds_rtplus_group(enc, blocks);
|
||||
} else {
|
||||
get_rds_oda_group(blocks);
|
||||
get_rds_oda_group(enc, blocks);
|
||||
}
|
||||
rds_state.rtp_oda ^= 1;
|
||||
enc->state[enc->program].rtp_oda ^= 1;
|
||||
goto group_coded;
|
||||
// TODO: add uecp
|
||||
case '3':
|
||||
get_rds_oda_group(blocks);
|
||||
get_rds_oda_group(enc, blocks);
|
||||
goto group_coded;
|
||||
case 'F':
|
||||
get_rds_lps_group(blocks);
|
||||
get_rds_lps_group(enc, blocks);
|
||||
goto group_coded;
|
||||
}
|
||||
|
||||
|
||||
group_coded:
|
||||
if (IS_TYPE_B(blocks)) {
|
||||
blocks[2] = rds_data.pi;
|
||||
blocks[2] = enc->data[enc->program].pi;
|
||||
}
|
||||
}
|
||||
|
||||
void get_rds_bits(uint8_t *bits) {
|
||||
void get_rds_bits(RDSEncoder* enc, uint8_t *bits) {
|
||||
static uint16_t out_blocks[GROUP_LENGTH];
|
||||
get_rds_group(out_blocks);
|
||||
add_checkwords(out_blocks, bits);
|
||||
get_rds_group(enc, out_blocks);
|
||||
add_checkwords(enc, out_blocks, bits);
|
||||
}
|
||||
|
||||
static void init_rtplus(uint8_t group) {
|
||||
register_oda(group, ODA_AID_RTPLUS, 0);
|
||||
rtplus_cfg.group = group;
|
||||
rtplus_cfg.enabled = 0;
|
||||
static void init_rtplus(RDSEncoder* enc, uint8_t group) {
|
||||
register_oda(enc, group, ODA_AID_RTPLUS, 0);
|
||||
enc->rtpData[enc->program].group = group;
|
||||
enc->rtpData[enc->program].enabled = 0;
|
||||
}
|
||||
|
||||
void init_rds_encoder(struct rds_params_t rds_params) {
|
||||
void init_rds_encoder(RDSEncoder* enc) {
|
||||
memset(&enc->data[enc->program].af, 0, sizeof(RDSAFs));
|
||||
enc->data[enc->program].ct = 1;
|
||||
enc->data[enc->program].di = 1;
|
||||
enc->data[enc->program].ecclic_enabled = 1;
|
||||
enc->data[enc->program].grp_sqc = (unsigned char*)"002222\0";
|
||||
enc->data[enc->program].ms = 1;
|
||||
enc->data[enc->program].pi = 0xFFFF;
|
||||
enc->data[enc->program].ps = (unsigned char*)"* RDS *";
|
||||
enc->data[enc->program].rt1_enabled = 1;
|
||||
|
||||
/* AF */
|
||||
if (rds_params.af.num_afs) {
|
||||
set_rds_af(rds_params.af);
|
||||
fprintf(stderr, show_af_list(rds_params.af));
|
||||
}
|
||||
|
||||
set_rds_pi(rds_params.pi);
|
||||
set_rds_ps(rds_params.ps);
|
||||
rds_state.rt_ab = 1;
|
||||
set_rds_shortrt(rds_params.shortrt);
|
||||
set_rds_rt1_enabled(rds_params.rt1_enabled);
|
||||
set_rds_rt1(rds_params.rt1);
|
||||
set_rds_pty(rds_params.pty);
|
||||
rds_state.ptyn_ab = 1;
|
||||
set_rds_ptyn(rds_params.ptyn);
|
||||
set_rds_lps(rds_params.lps);
|
||||
set_rds_tp(rds_params.tp);
|
||||
set_rds_ecc(rds_params.ecc);
|
||||
set_rds_lic(rds_params.lic);
|
||||
set_rds_ecclic_toggle(1);
|
||||
set_rds_pin_enabled(1);
|
||||
set_rds_ct(1);
|
||||
set_rds_ms(1);
|
||||
set_rds_di(DI_STEREO | DI_DPTY);
|
||||
set_rds_grpseq(rds_params.grp_sqc);
|
||||
set_rds_udg1(rds_params.udg1_len, rds_params.udg1);
|
||||
set_rds_udg2(rds_params.udg2_len, rds_params.udg2);
|
||||
enc->state[enc->program].rt_ab = 1;
|
||||
enc->state[enc->program].ptyn_ab = 1;
|
||||
|
||||
init_rtplus(GROUP_11A);
|
||||
|
||||
if(fileExists("~/.rdsEncoder")) {
|
||||
loadFromFile("~/.rdsEncoder", &enc);
|
||||
} else {
|
||||
saveToFile("~/.rdsEncoder")
|
||||
}
|
||||
|
||||
init_rds_objects();
|
||||
}
|
||||
|
||||
void set_rds_pi(uint16_t pi_code) {
|
||||
rds_data.pi = pi_code;
|
||||
}
|
||||
|
||||
void set_rds_ecc(uint8_t ecc) {
|
||||
rds_data.ecc = ecc;
|
||||
}
|
||||
|
||||
void set_rds_lic(uint8_t lic) {
|
||||
rds_data.lic = lic & INT16_L12;
|
||||
}
|
||||
|
||||
void set_rds_ecclic_toggle(uint8_t toggle) {
|
||||
rds_state.ecclic_enabled = toggle & INT8_0;
|
||||
}
|
||||
|
||||
void set_rds_pin_enabled(uint8_t enabled) {
|
||||
rds_data.pin[0] = enabled & 1;
|
||||
}
|
||||
|
||||
void set_rds_pin(uint8_t day, uint8_t hour, uint8_t minute) {
|
||||
rds_data.pin[1] = (day & INT8_L5);
|
||||
rds_data.pin[2] = (hour & INT8_L5);
|
||||
rds_data.pin[3] = (minute & INT8_L6);
|
||||
}
|
||||
|
||||
void set_rds_shortrt(uint8_t shortrt) {
|
||||
rds_data.shortrt = shortrt & INT8_0;
|
||||
}
|
||||
void set_rds_rt1_enabled(uint8_t rt1en) {
|
||||
rds_data.rt1_enabled = rt1en & INT8_0;
|
||||
}
|
||||
void set_rds_rt1(unsigned char *rt1) {
|
||||
void set_rds_rt1(RDSEncoder* enc, unsigned char *rt1) {
|
||||
uint8_t i = 0, len = 0;
|
||||
|
||||
rds_state.rt_update = 1;
|
||||
enc->state[enc->program].rt_update = 1;
|
||||
|
||||
memset(rds_data.rt1, ' ', RT_LENGTH);
|
||||
while (*rt1 != 0 && len < RT_LENGTH)
|
||||
rds_data.rt1[len++] = *rt1++;
|
||||
memset(enc->data[enc->program].rt1, ' ', RT_LENGTH);
|
||||
while (*rt1 != 0 && len < RT_LENGTH) enc->data[enc->program].rt1[len++] = *rt1++;
|
||||
|
||||
if (len < RT_LENGTH && rds_data.shortrt) {
|
||||
rds_state.rt_segments = 0;
|
||||
if (len < RT_LENGTH && enc->data[enc->program].shortrt) {
|
||||
enc->state[enc->program].rt_segments = 0;
|
||||
|
||||
rds_data.rt1[len++] = '\r';
|
||||
enc->data[enc->program].rt1[len++] = '\r';
|
||||
|
||||
while (i < len) {
|
||||
i += 4;
|
||||
rds_state.rt_segments++;
|
||||
enc->state[enc->program].rt_segments++;
|
||||
}
|
||||
} else {
|
||||
rds_state.rt_segments = 16;
|
||||
enc->state[enc->program].rt_segments = 16;
|
||||
}
|
||||
}
|
||||
|
||||
void set_rds_ps(unsigned char *ps) {
|
||||
void set_rds_ps(RDSEncoder* enc, unsigned char *ps) {
|
||||
uint8_t len = 0;
|
||||
|
||||
rds_state.ps_update = 1;
|
||||
memset(rds_data.ps, ' ', PS_LENGTH);
|
||||
while (*ps != 0 && len < PS_LENGTH)
|
||||
rds_data.ps[len++] = *ps++;
|
||||
enc->state[enc->program].ps_update = 1;
|
||||
memset(enc->data[enc->program].ps, ' ', PS_LENGTH);
|
||||
while (*ps != 0 && len < PS_LENGTH) enc->data[enc->program].ps[len++] = *ps++;
|
||||
}
|
||||
void set_rds_tps(unsigned char *tps) {
|
||||
|
||||
void set_rds_tps(RDSEncoder* enc, unsigned char *tps) {
|
||||
uint8_t len = 0;
|
||||
|
||||
rds_state.tps_update = 1;
|
||||
enc->state[enc->program].tps_update = 1;
|
||||
if(tps[0] == '\0') {
|
||||
memset(rds_data.tps, 0, PS_LENGTH);
|
||||
memset(enc->data[enc->program].tps, 0, PS_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(rds_data.tps, ' ', PS_LENGTH);
|
||||
while (*tps != 0 && len < PS_LENGTH)
|
||||
rds_data.tps[len++] = *tps++;
|
||||
memset(enc->data[enc->program].tps, ' ', PS_LENGTH);
|
||||
while (*tps != 0 && len < PS_LENGTH) enc->data[enc->program].tps[len++] = *tps++;
|
||||
}
|
||||
|
||||
void set_rds_lps(unsigned char *lps) {
|
||||
void set_rds_lps(RDSEncoder* enc, unsigned char *lps) {
|
||||
uint8_t i = 0, len = 0;
|
||||
|
||||
rds_state.lps_update = 1;
|
||||
enc->state[enc->program].lps_update = 1;
|
||||
if(lps[0] == '\0') {
|
||||
memset(rds_data.lps, 0, LPS_LENGTH);
|
||||
memset(enc->data[enc->program].lps, 0, LPS_LENGTH);
|
||||
return;
|
||||
}
|
||||
memset(rds_data.lps, '\r', LPS_LENGTH);
|
||||
while (*lps != 0 && len < LPS_LENGTH)
|
||||
rds_data.lps[len++] = *lps++;
|
||||
memset(enc->data[enc->program].lps, '\r', LPS_LENGTH);
|
||||
while (*lps != 0 && len < LPS_LENGTH) enc->data[enc->program].lps[len++] = *lps++;
|
||||
|
||||
if (len < LPS_LENGTH) {
|
||||
rds_state.lps_segments = 0;
|
||||
enc->state[enc->program].lps_segments = 0;
|
||||
|
||||
len++;
|
||||
|
||||
while (i < len) {
|
||||
i += 4;
|
||||
rds_state.lps_segments++;
|
||||
enc->state[enc->program].lps_segments++;
|
||||
}
|
||||
} else {
|
||||
rds_state.lps_segments = 8;
|
||||
enc->state[enc->program].lps_segments = 8;
|
||||
}
|
||||
}
|
||||
|
||||
void set_rds_rtplus_flags(uint8_t flags) {
|
||||
rtplus_cfg.enabled = (flags==2);
|
||||
rtplus_cfg.running = flags & INT8_0;
|
||||
void set_rds_rtplus_flags(RDSEncoder* enc, uint8_t flags) {
|
||||
enc->rtpData[enc->program].enabled = (flags==2);
|
||||
enc->rtpData[enc->program].running = flags & INT8_0;
|
||||
}
|
||||
|
||||
void set_rds_rtplus_tags(uint8_t *tags) {
|
||||
rtplus_cfg.type[0] = tags[0] & INT8_L6;
|
||||
rtplus_cfg.start[0] = tags[1] & INT8_L6;
|
||||
rtplus_cfg.len[0] = tags[2] & INT8_L6;
|
||||
rtplus_cfg.type[1] = tags[3] & INT8_L6;
|
||||
rtplus_cfg.start[1] = tags[4] & INT8_L6;
|
||||
rtplus_cfg.len[1] = tags[5] & INT8_L5;
|
||||
void set_rds_rtplus_tags(RDSEncoder* enc, uint8_t *tags) {
|
||||
enc->rtpData[enc->program].type[0] = tags[0] & INT8_L6;
|
||||
enc->rtpData[enc->program].start[0] = tags[1] & INT8_L6;
|
||||
enc->rtpData[enc->program].len[0] = tags[2] & INT8_L6;
|
||||
enc->rtpData[enc->program].type[1] = tags[3] & INT8_L6;
|
||||
enc->rtpData[enc->program].start[1] = tags[4] & INT8_L6;
|
||||
enc->rtpData[enc->program].len[1] = tags[5] & INT8_L5;
|
||||
|
||||
rtplus_cfg.toggle ^= 1;
|
||||
rtplus_cfg.running = 1;
|
||||
rtplus_cfg.enabled = 1;
|
||||
enc->rtpData[enc->program].toggle ^= 1;
|
||||
enc->rtpData[enc->program].running = 1;
|
||||
enc->rtpData[enc->program].enabled = 1;
|
||||
}
|
||||
|
||||
void set_rds_af(struct rds_af_t new_af_list) {
|
||||
memcpy(&rds_data.af, &new_af_list, sizeof(struct rds_af_t));
|
||||
}
|
||||
|
||||
void clear_rds_af() {
|
||||
memset(&rds_data.af, 0, sizeof(struct rds_af_t));
|
||||
}
|
||||
|
||||
void set_rds_pty(uint8_t pty) {
|
||||
rds_data.pty = pty & 31;
|
||||
}
|
||||
|
||||
void set_rds_ptyn_enabled(uint8_t enabled) {
|
||||
rds_state.ptyn_enabled = enabled & 1;
|
||||
}
|
||||
|
||||
void set_rds_ptyn(unsigned char *ptyn) {
|
||||
void set_rds_ptyn(RDSEncoder* enc, unsigned char *ptyn) {
|
||||
uint8_t len = 0;
|
||||
|
||||
rds_state.ptyn_update = 1;
|
||||
enc->state[enc->program].ptyn_update = 1;
|
||||
|
||||
if(ptyn[0] == '\0') {
|
||||
memset(rds_data.ptyn, 0, PTYN_LENGTH);
|
||||
memset(enc->data[enc->program].ptyn, 0, PTYN_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(rds_data.ptyn, ' ', PTYN_LENGTH);
|
||||
while (*ptyn != 0 && len < PTYN_LENGTH)
|
||||
rds_data.ptyn[len++] = *ptyn++;
|
||||
}
|
||||
|
||||
void set_rds_ta(uint8_t ta) {
|
||||
rds_data.ta = ta & INT8_0;
|
||||
}
|
||||
|
||||
void set_rds_tp(uint8_t tp) {
|
||||
rds_data.tp = tp & INT8_0;
|
||||
}
|
||||
|
||||
void set_rds_ms(uint8_t ms) {
|
||||
rds_data.ms = ms & INT8_0;
|
||||
}
|
||||
|
||||
void set_rds_di(uint8_t di) {
|
||||
rds_data.di = di & INT8_L4;
|
||||
}
|
||||
|
||||
void set_rds_ct(uint8_t ct) {
|
||||
rds_data.ct = ct & INT8_0;
|
||||
}
|
||||
|
||||
void set_rds_cg(uint16_t* blocks) {
|
||||
rds_state.custom_group[0] = 1;
|
||||
rds_state.custom_group[1] = blocks[0];
|
||||
rds_state.custom_group[2] = blocks[1];
|
||||
rds_state.custom_group[3] = blocks[2];
|
||||
}
|
||||
|
||||
void set_rds_grpseq(unsigned char* grpseq) {
|
||||
uint8_t len = 0;
|
||||
memset(rds_data.grp_sqc, 0, 24);
|
||||
while (*grpseq != 0 && len < 24)
|
||||
rds_data.grp_sqc[len++] = *grpseq++;
|
||||
}
|
||||
|
||||
void set_rds_udg1(uint8_t len, uint16_t (*groups)[3]) {
|
||||
rds_data.udg1_len = len;
|
||||
if (len > 0 && groups != NULL) {
|
||||
memcpy(rds_data.udg1, groups, len * sizeof(uint16_t[3]));
|
||||
}
|
||||
}
|
||||
|
||||
void set_rds_udg2(uint8_t len, uint16_t (*groups)[3]) {
|
||||
rds_data.udg2_len = len;
|
||||
if (len > 0 && groups != NULL) {
|
||||
memcpy(rds_data.udg2, groups, len * sizeof(uint16_t[3]));
|
||||
}
|
||||
memset(enc->data[enc->program].ptyn, ' ', PTYN_LENGTH);
|
||||
while (*ptyn != 0 && len < PTYN_LENGTH) enc->data[enc->program].ptyn[len++] = *ptyn++;
|
||||
}
|
||||
137
src/rds.h
137
src/rds.h
@@ -22,21 +22,26 @@
|
||||
|
||||
#define MAX_AFS 25
|
||||
|
||||
typedef struct rds_af_t {
|
||||
typedef struct {
|
||||
uint8_t num_entries;
|
||||
uint8_t num_afs;
|
||||
uint8_t afs[MAX_AFS];
|
||||
} rds_af_t;
|
||||
} RDSAFs;
|
||||
|
||||
#define AF_CODE_FILLER 205
|
||||
#define AF_CODE_NO_AF 224
|
||||
#define AF_CODE_NUM_AFS_BASE AF_CODE_NO_AF
|
||||
#define AF_CODE_LFMF_FOLLOWS 250
|
||||
|
||||
typedef struct rds_params_t {
|
||||
#define PROGRAMS 1
|
||||
|
||||
typedef struct {
|
||||
uint16_t pi;
|
||||
|
||||
uint8_t ecclic_enabled;
|
||||
uint16_t lic;
|
||||
uint8_t ecc;
|
||||
|
||||
uint8_t ta;
|
||||
uint8_t pty;
|
||||
uint8_t tp;
|
||||
@@ -52,21 +57,81 @@ typedef struct rds_params_t {
|
||||
|
||||
unsigned char ptyn[PTYN_LENGTH];
|
||||
|
||||
struct rds_af_t af;
|
||||
RDSAFs af;
|
||||
|
||||
uint8_t ct;
|
||||
|
||||
unsigned char lps[LPS_LENGTH];
|
||||
|
||||
// Enabled, day, hour, minute
|
||||
uint8_t pin[4];
|
||||
|
||||
unsigned char grp_sqc[24];
|
||||
|
||||
uint8_t udg1_len;
|
||||
uint8_t udg2_len;
|
||||
|
||||
uint16_t udg1[8][3];
|
||||
uint16_t udg2[8][3];
|
||||
} rds_params_t;
|
||||
} RDSData;
|
||||
typedef struct {
|
||||
uint8_t ecc_or_lic;
|
||||
|
||||
uint8_t ps_update;
|
||||
uint8_t tps_update;
|
||||
|
||||
uint8_t rt_update;
|
||||
uint8_t rt_ab;
|
||||
uint8_t rt_segments;
|
||||
|
||||
uint8_t ptyn_enabled;
|
||||
uint8_t ptyn_update;
|
||||
uint8_t ptyn_ab;
|
||||
|
||||
uint8_t lps_update;
|
||||
uint8_t lps_segments;
|
||||
|
||||
uint16_t custom_group[GROUP_LENGTH];
|
||||
|
||||
uint8_t rtp_oda;
|
||||
uint8_t grp_seq_idx[2];
|
||||
uint8_t udg_idxs[2];
|
||||
} RDSState;
|
||||
|
||||
#define MAX_ODAS 8
|
||||
|
||||
// List of ODAs: https://www.nrscstandards.org/committees/dsm/archive/rds-oda-aids.pdf
|
||||
#define ODA_AID_RTPLUS 0x4bd7
|
||||
typedef struct rds_oda_t {
|
||||
uint8_t group;
|
||||
uint16_t aid;
|
||||
uint16_t scb;
|
||||
} RDSODA;
|
||||
|
||||
typedef struct {
|
||||
uint8_t current;
|
||||
uint8_t count;
|
||||
} RDSODAState;
|
||||
|
||||
typedef struct {
|
||||
uint8_t group;
|
||||
uint8_t enabled;
|
||||
uint8_t running;
|
||||
uint8_t toggle;
|
||||
uint8_t type[2];
|
||||
uint8_t start[2];
|
||||
uint8_t len[2];
|
||||
} RDSRTPlusData;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
RDSData data[PROGRAMS];
|
||||
RDSState state[PROGRAMS];
|
||||
RDSODA odas[PROGRAMS][MAX_ODAS];
|
||||
RDSODAState oda_state[PROGRAMS];
|
||||
RDSRTPlusData rtpData[PROGRAMS];
|
||||
uint8_t program;
|
||||
} RDSEncoder;
|
||||
|
||||
#define GROUP_TYPE_0 ( 0 << 4)
|
||||
#define GROUP_TYPE_1 ( 1 << 4)
|
||||
@@ -214,58 +279,14 @@ typedef struct rds_params_t {
|
||||
|
||||
#define IS_TYPE_B(a) (a[1] & INT16_11)
|
||||
|
||||
/*
|
||||
* RDS ODA ID group
|
||||
*
|
||||
* This struct is for defining ODAs that will be transmitted
|
||||
*
|
||||
* Can signal version A or B data groups
|
||||
*/
|
||||
typedef struct rds_oda_t {
|
||||
uint8_t group;
|
||||
uint16_t aid;
|
||||
uint16_t scb;
|
||||
} rds_oda_t;
|
||||
|
||||
#define MAX_ODAS 8
|
||||
|
||||
/*
|
||||
* ODA AID
|
||||
*
|
||||
* Extensive list: https://www.nrscstandards.org/committees/dsm/archive/rds-oda-aids.pdf
|
||||
*/
|
||||
#define ODA_AID_RTPLUS 0x4bd7
|
||||
|
||||
extern void init_rds_encoder(struct rds_params_t rds_params);
|
||||
extern void get_rds_bits(uint8_t *bits);
|
||||
extern void set_rds_pi(uint16_t pi_code);
|
||||
extern void set_rds_ecc(uint8_t ecc);
|
||||
extern void set_rds_lic(uint8_t lic);
|
||||
extern void set_rds_ecclic_toggle(uint8_t toggle);
|
||||
extern void set_rds_pin_enabled(uint8_t enabled);
|
||||
extern void set_rds_pin(uint8_t day, uint8_t hour, uint8_t minute);
|
||||
extern void set_rds_shortrt(uint8_t shortrt);
|
||||
extern void set_rds_rt1_enabled(uint8_t rt1en);
|
||||
extern void set_rds_rt1(unsigned char *rt1);
|
||||
extern void set_rds_ps(unsigned char *ps);
|
||||
extern void set_rds_tps(unsigned char *ps);
|
||||
extern void set_rds_lps(unsigned char *lps);
|
||||
extern void set_rds_rtplus_flags(uint8_t flags);
|
||||
extern void set_rds_rtplus_tags(uint8_t *tags);
|
||||
extern void set_rds_ta(uint8_t ta);
|
||||
extern void set_rds_pty(uint8_t pty);
|
||||
extern void set_rds_ptyn_enabled(uint8_t enabled);
|
||||
extern void set_rds_ptyn(unsigned char *ptyn);
|
||||
extern void set_rds_af(struct rds_af_t new_af_list);
|
||||
extern void clear_rds_af();
|
||||
extern void set_rds_tp(uint8_t tp);
|
||||
extern void set_rds_ms(uint8_t ms);
|
||||
extern void set_rds_ct(uint8_t ct);
|
||||
extern void set_rds_di(uint8_t di);
|
||||
extern float get_rds_sample();
|
||||
extern void set_rds_cg(uint16_t* blocks);
|
||||
extern void set_rds_grpseq(unsigned char* grpseq);
|
||||
extern void set_rds_udg1(uint8_t len, uint16_t (*groups)[3]);
|
||||
extern void set_rds_udg2(uint8_t len, uint16_t (*groups)[3]);
|
||||
void init_rds_encoder(RDSEncoder* enc);
|
||||
void get_rds_bits(RDSEncoder* enc, uint8_t *bits);
|
||||
void set_rds_rt1(RDSEncoder* enc, unsigned char *rt1);
|
||||
void set_rds_ps(RDSEncoder* enc, unsigned char *ps);
|
||||
void set_rds_tps(RDSEncoder* enc, unsigned char *tps);
|
||||
void set_rds_lps(RDSEncoder* enc, unsigned char *lps);
|
||||
void set_rds_rtplus_flags(RDSEncoder* enc, uint8_t flags);
|
||||
void set_rds_rtplus_tags(RDSEncoder* enc, uint8_t *tags);
|
||||
void set_rds_ptyn(RDSEncoder* enc, unsigned char *ptyn);
|
||||
|
||||
#endif /* RDS_H */
|
||||
|
||||
98
src/rds95.c
98
src/rds95.c
@@ -6,6 +6,7 @@
|
||||
#include <pulse/error.h>
|
||||
|
||||
#include "rds.h"
|
||||
#include "modulator.h"
|
||||
#include "control_pipe.h"
|
||||
#include "lib.h"
|
||||
#include "ascii_cmd.h"
|
||||
@@ -20,9 +21,10 @@ static void stop() {
|
||||
}
|
||||
|
||||
/* threads */
|
||||
static void *control_pipe_worker() {
|
||||
static void *control_pipe_worker(void* encoder) {
|
||||
RDSModulator *enc = (RDSModulator*)encoder;
|
||||
while (!stop_rds) {
|
||||
poll_control_pipe();
|
||||
poll_control_pipe(enc);
|
||||
msleep(READ_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
@@ -36,22 +38,6 @@ static void show_help(char *name) {
|
||||
"\n"
|
||||
"Usage: %s [options]\n"
|
||||
"\n"
|
||||
" -i,--pi Program Identification code\n"
|
||||
" [default: 305F]\n"
|
||||
" -s,--ps Program Service name\n"
|
||||
" [default: \"radio95\"]\n"
|
||||
" -r,--rt1 Radio Text 1\n"
|
||||
" [default: (nothing)]\n"
|
||||
" -p,--pty Program Type\n"
|
||||
" [default: 0]\n"
|
||||
" -T,--tp Traffic Program\n"
|
||||
" [default: 0]\n"
|
||||
" -A,--af Alternative Frequency (FM/LF/MF)\n"
|
||||
" (more than one AF may be passed)\n"
|
||||
" -P,--ptyn Program Type Name\n"
|
||||
" -l,--lps Long PS\n"
|
||||
" -e,--ecc ECC code\n"
|
||||
" -d,--di DI code\n"
|
||||
" -C,--ctl FIFO control pipe\n"
|
||||
" -h,--help Show this help text and exit\n"
|
||||
"\n",
|
||||
@@ -62,16 +48,6 @@ static void show_help(char *name) {
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char control_pipe[51] = "\0";
|
||||
struct rds_params_t rds_params = {
|
||||
.ps = "radio95",
|
||||
.rt1 = "",
|
||||
.pi = 0x305F,
|
||||
.ecc = 0xE2,
|
||||
.lps = "radio95 - Radio Nowotomyskie",
|
||||
.grp_sqc = "00012222FFR",
|
||||
.shortrt = 1,
|
||||
.rt1_enabled = 1
|
||||
};
|
||||
/* PASIMPLE */
|
||||
pa_simple *device;
|
||||
pa_sample_spec format;
|
||||
@@ -80,22 +56,10 @@ int main(int argc, char **argv) {
|
||||
pthread_attr_t attr;
|
||||
pthread_t control_pipe_thread;
|
||||
|
||||
const char *short_opt = "R:i:s:r:p:T:A:P:l:e:L:d:C:h";
|
||||
const char *short_opt = "C:h";
|
||||
|
||||
struct option long_opt[] =
|
||||
{
|
||||
{"rds", required_argument, NULL, 'R'},
|
||||
{"pi", required_argument, NULL, 'i'},
|
||||
{"ps", required_argument, NULL, 's'},
|
||||
{"rt1", required_argument, NULL, 'r'},
|
||||
{"pty", required_argument, NULL, 'p'},
|
||||
{"tp", required_argument, NULL, 'T'},
|
||||
{"af", required_argument, NULL, 'A'},
|
||||
{"ptyn", required_argument, NULL, 'P'},
|
||||
{"lps", required_argument, NULL, 'l'},
|
||||
{"ecc", required_argument, NULL, 'e'},
|
||||
{"lic", required_argument, NULL, 'L'},
|
||||
{"di", required_argument, NULL, 'd'},
|
||||
{"ctl", required_argument, NULL, 'C'},
|
||||
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
@@ -105,46 +69,6 @@ int main(int argc, char **argv) {
|
||||
int opt;
|
||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'i': /* pi */
|
||||
rds_params.pi = strtoul(optarg, NULL, 16);
|
||||
break;
|
||||
|
||||
case 's': /* ps */
|
||||
memcpy(rds_params.ps, xlat((unsigned char *)optarg), PS_LENGTH);
|
||||
break;
|
||||
|
||||
case 'r': /* rt1 */
|
||||
memcpy(rds_params.rt1, xlat((unsigned char *)optarg), RT_LENGTH);
|
||||
break;
|
||||
|
||||
case 'p': /* pty */
|
||||
rds_params.pty = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
|
||||
case 'T': /* tp */
|
||||
rds_params.tp = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
|
||||
case 'A': /* af */
|
||||
if (add_rds_af(&rds_params.af, strtof(optarg, NULL)) == 1) return 1;
|
||||
break;
|
||||
|
||||
case 'P': /* ptyn */
|
||||
memcpy(rds_params.ptyn, xlat((unsigned char *)optarg), PTYN_LENGTH);
|
||||
break;
|
||||
|
||||
case 'l': /* lps */
|
||||
memcpy(rds_params.lps, (unsigned char *)optarg, LPS_LENGTH);
|
||||
break;
|
||||
|
||||
case 'e': /* ecc */
|
||||
rds_params.ecc = strtoul(optarg, NULL, 16);
|
||||
break;
|
||||
|
||||
case 'L': /* lic */
|
||||
rds_params.lic = strtoul(optarg, NULL, 16);
|
||||
break;
|
||||
|
||||
case 'C': /* ctl */
|
||||
memcpy(control_pipe, optarg, 50);
|
||||
break;
|
||||
@@ -163,9 +87,6 @@ int main(int argc, char **argv) {
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
|
||||
/* Initialize the RDS modulator */
|
||||
init_rds_encoder(rds_params);
|
||||
|
||||
/* PASIMPLE format */
|
||||
format.format = PA_SAMPLE_FLOAT32NE;
|
||||
format.channels = 1;
|
||||
@@ -187,11 +108,16 @@ int main(int argc, char **argv) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
RDSEncoder rdsEncoder;
|
||||
RDSModulator rdsModulator;
|
||||
init_rds_encoder(&rdsEncoder);
|
||||
init_rds_modulator(&rdsModulator, &rdsEncoder);
|
||||
|
||||
if (control_pipe[0]) {
|
||||
if (open_control_pipe(control_pipe) == 0) {
|
||||
fprintf(stderr, "Reading control commands on %s.\n", control_pipe);
|
||||
int r;
|
||||
r = pthread_create(&control_pipe_thread, &attr, control_pipe_worker, NULL);
|
||||
r = pthread_create(&control_pipe_thread, &attr, control_pipe_worker, (void*)&rdsModulator);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Could not create control pipe thread.\n");
|
||||
control_pipe[0] = 0;
|
||||
@@ -211,7 +137,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
while(!stop_rds) {
|
||||
for (uint16_t i = 0; i < NUM_MPX_FRAMES; i++) {
|
||||
mpx_buffer[i] = get_rds_sample();
|
||||
mpx_buffer[i] = get_rds_sample(&rdsModulator);
|
||||
}
|
||||
|
||||
if (pa_simple_write(device, mpx_buffer, sizeof(mpx_buffer), &pulse_error) != 0) {
|
||||
|
||||
Reference in New Issue
Block a user