mirror of
https://github.com/radio95-rnt/rds95.git
synced 2026-02-27 04:43:52 +01:00
refactor RDS modulator parameters and file handling; remove deprecated commands
This commit is contained in:
@@ -12,7 +12,6 @@ typedef struct {
|
|||||||
uint8_t cmd_length;
|
uint8_t cmd_length;
|
||||||
} command_handler_t;
|
} command_handler_t;
|
||||||
|
|
||||||
// Command handlers
|
|
||||||
static void handle_ptyn(unsigned char *arg, RDSModulator* mod) {
|
static void handle_ptyn(unsigned char *arg, RDSModulator* mod) {
|
||||||
arg[PTYN_LENGTH] = 0;
|
arg[PTYN_LENGTH] = 0;
|
||||||
set_rds_ptyn(mod->enc, xlat(arg));
|
set_rds_ptyn(mod->enc, xlat(arg));
|
||||||
@@ -243,11 +242,11 @@ static void handle_grpseq(unsigned char *arg, RDSModulator* mod) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void handle_level(unsigned char *arg, RDSModulator* mod) {
|
static void handle_level(unsigned char *arg, RDSModulator* mod) {
|
||||||
mod->level = strtoul((char *)arg, NULL, 10)/255.0f;
|
mod->params.level = strtoul((char *)arg, NULL, 10)/255.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_rdsgen(unsigned char *arg, RDSModulator* mod) {
|
static void handle_rdsgen(unsigned char *arg, RDSModulator* mod) {
|
||||||
mod->rdsgen = strtoul((char *)arg, NULL, 10);
|
mod->params.rdsgen = strtoul((char *)arg, NULL, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_udg1(unsigned char *arg, RDSModulator* mod) {
|
static void handle_udg1(unsigned char *arg, RDSModulator* mod) {
|
||||||
@@ -280,7 +279,7 @@ static void handle_udg1(unsigned char *arg, RDSModulator* mod) {
|
|||||||
mod->enc->data[mod->enc->program].udg1_len = sets;
|
mod->enc->data[mod->enc->program].udg1_len = sets;
|
||||||
}
|
}
|
||||||
static void handle_udg2(unsigned char *arg, RDSModulator* mod) {
|
static void handle_udg2(unsigned char *arg, RDSModulator* mod) {
|
||||||
uint16_t blocks[8][3]; // Up to 8 sets of 3 blocks each
|
uint16_t blocks[8][3];
|
||||||
int sets = 0;
|
int sets = 0;
|
||||||
unsigned char *ptr = arg;
|
unsigned char *ptr = arg;
|
||||||
|
|
||||||
@@ -289,20 +288,19 @@ static void handle_udg2(unsigned char *arg, RDSModulator* mod) {
|
|||||||
&blocks[sets][0], &blocks[sets][1], &blocks[sets][2]);
|
&blocks[sets][0], &blocks[sets][1], &blocks[sets][2]);
|
||||||
|
|
||||||
if (count != 3) {
|
if (count != 3) {
|
||||||
break; // Couldn't parse a complete set of 3 blocks
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sets++;
|
sets++;
|
||||||
|
|
||||||
// Look for the comma separator
|
|
||||||
while (*ptr && *ptr != ',') {
|
while (*ptr && *ptr != ',') {
|
||||||
ptr++;
|
ptr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*ptr == ',') {
|
if (*ptr == ',') {
|
||||||
ptr++; // Skip over the comma
|
ptr++;
|
||||||
} else {
|
} else {
|
||||||
break; // No more separators
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,15 +313,14 @@ static void handle_init(unsigned char *arg, RDSModulator* mod) {
|
|||||||
set_rds_defaults(mod->enc, mod->enc->program);
|
set_rds_defaults(mod->enc, mod->enc->program);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command tables organized by delimiter position and command length
|
|
||||||
static const command_handler_t commands_eq3[] = {
|
static const command_handler_t commands_eq3[] = {
|
||||||
|
{"MS", handle_ms, 2},
|
||||||
{"PS", handle_ps, 2},
|
{"PS", handle_ps, 2},
|
||||||
{"CT", handle_ct, 2},
|
{"PI", handle_pi, 2},
|
||||||
{"DI", handle_di, 2},
|
|
||||||
{"TP", handle_tp, 2},
|
{"TP", handle_tp, 2},
|
||||||
{"TA", handle_ta, 2},
|
{"TA", handle_ta, 2},
|
||||||
{"MS", handle_ms, 2},
|
{"DI", handle_di, 2},
|
||||||
{"PI", handle_pi, 2},
|
{"CT", handle_ct, 2},
|
||||||
{"AF", handle_af, 2}
|
{"AF", handle_af, 2}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -374,7 +371,6 @@ static const command_handler_t commands_exact[] = {
|
|||||||
// TODO: handle help, ver, status
|
// TODO: handle help, ver, status
|
||||||
};
|
};
|
||||||
|
|
||||||
// Process a command using the appropriate command table
|
|
||||||
static bool process_command_table(const command_handler_t *table, int table_size,
|
static bool process_command_table(const command_handler_t *table, int table_size,
|
||||||
unsigned char *cmd, unsigned char *arg, RDSModulator* mod) {
|
unsigned char *cmd, unsigned char *arg, RDSModulator* mod) {
|
||||||
for (int i = 0; i < table_size; i++) {
|
for (int i = 0; i < table_size; i++) {
|
||||||
@@ -404,6 +400,7 @@ void process_ascii_cmd(RDSModulator* mod, unsigned char *str) {
|
|||||||
char option[32] = {0};
|
char option[32] = {0};
|
||||||
snprintf(option, sizeof(option), "%s", (const char*)str);
|
snprintf(option, sizeof(option), "%s", (const char*)str);
|
||||||
saveToFile(mod->enc, option);
|
saveToFile(mod->enc, option);
|
||||||
|
Modulator_saveToFile(mod->params, option);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,7 +415,6 @@ void process_ascii_cmd(RDSModulator* mod, unsigned char *str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process commands with = delimiter at position 3 (format: XX=y...)
|
|
||||||
if (cmd_len > 2 && str[2] == '=') {
|
if (cmd_len > 2 && str[2] == '=') {
|
||||||
cmd = str;
|
cmd = str;
|
||||||
cmd[2] = 0;
|
cmd[2] = 0;
|
||||||
@@ -430,7 +426,6 @@ void process_ascii_cmd(RDSModulator* mod, unsigned char *str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process commands with = delimiter at position 4 (format: XXX=y...)
|
|
||||||
if (cmd_len > 3 && str[3] == '=') {
|
if (cmd_len > 3 && str[3] == '=') {
|
||||||
cmd = str;
|
cmd = str;
|
||||||
cmd[3] = 0;
|
cmd[3] = 0;
|
||||||
@@ -442,7 +437,6 @@ void process_ascii_cmd(RDSModulator* mod, unsigned char *str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process commands with = delimiter at position 5 (format: XXXX=y...)
|
|
||||||
if (cmd_len > 4 && str[4] == '=') {
|
if (cmd_len > 4 && str[4] == '=') {
|
||||||
cmd = str;
|
cmd = str;
|
||||||
cmd[4] = 0;
|
cmd[4] = 0;
|
||||||
@@ -454,7 +448,6 @@ void process_ascii_cmd(RDSModulator* mod, unsigned char *str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process commands with = delimiter at position 6 (format: XXXXX=y...)
|
|
||||||
if (cmd_len > 5 && str[5] == '=') {
|
if (cmd_len > 5 && str[5] == '=') {
|
||||||
cmd = str;
|
cmd = str;
|
||||||
cmd[5] = 0;
|
cmd[5] = 0;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* common includes for rds95 */
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -7,7 +6,6 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
/* workaround for missing pi definition */
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,40 +7,29 @@
|
|||||||
static int fd;
|
static int fd;
|
||||||
static struct pollfd poller;
|
static struct pollfd poller;
|
||||||
|
|
||||||
/*
|
|
||||||
* Opens a file (pipe) to be used to control the RDS coder.
|
|
||||||
*/
|
|
||||||
int open_control_pipe(char *filename) {
|
int open_control_pipe(char *filename) {
|
||||||
fd = open(filename, O_RDONLY | O_NONBLOCK);
|
fd = open(filename, O_RDONLY | O_NONBLOCK);
|
||||||
if (fd == -1) return -1;
|
if (fd == -1) return -1;
|
||||||
|
|
||||||
/* setup the poller */
|
|
||||||
poller.fd = fd;
|
poller.fd = fd;
|
||||||
poller.events = POLLIN;
|
poller.events = POLLIN;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Polls the control file (pipe), and if a command is received,
|
|
||||||
* calls process_ascii_cmd.
|
|
||||||
*/
|
|
||||||
void poll_control_pipe(RDSModulator* mod) {
|
void poll_control_pipe(RDSModulator* mod) {
|
||||||
static unsigned char pipe_buf[CTL_BUFFER_SIZE];
|
static unsigned char pipe_buf[CTL_BUFFER_SIZE];
|
||||||
static unsigned char cmd_buf[CMD_BUFFER_SIZE];
|
static unsigned char cmd_buf[CMD_BUFFER_SIZE];
|
||||||
int bytes_read;
|
int bytes_read;
|
||||||
char *token;
|
char *token;
|
||||||
|
|
||||||
/* check for new commands - return early if none */
|
|
||||||
if (poll(&poller, 1, READ_TIMEOUT_MS) <= 0) return;
|
if (poll(&poller, 1, READ_TIMEOUT_MS) <= 0) return;
|
||||||
if (!(poller.revents & POLLIN)) return;
|
if (!(poller.revents & POLLIN)) return;
|
||||||
|
|
||||||
/* read data from pipe */
|
|
||||||
memset(pipe_buf, 0, CTL_BUFFER_SIZE);
|
memset(pipe_buf, 0, CTL_BUFFER_SIZE);
|
||||||
bytes_read = read(fd, pipe_buf, CTL_BUFFER_SIZE - 1);
|
bytes_read = read(fd, pipe_buf, CTL_BUFFER_SIZE - 1);
|
||||||
|
|
||||||
if (bytes_read <= 0) return;
|
if (bytes_read <= 0) return;
|
||||||
|
|
||||||
/* process each command line */
|
|
||||||
token = strtok((char *)pipe_buf, "\n");
|
token = strtok((char *)pipe_buf, "\n");
|
||||||
while (token != NULL) {
|
while (token != NULL) {
|
||||||
size_t cmd_len = strlen(token);
|
size_t cmd_len = strlen(token);
|
||||||
|
|||||||
49
src/lib.c
49
src/lib.c
@@ -3,15 +3,13 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
extern int nanosleep(const struct timespec *req, struct timespec *rem);
|
extern int nanosleep(const struct timespec *req, struct timespec *rem);
|
||||||
/* millisecond sleep */
|
|
||||||
void msleep(unsigned long ms) {
|
void msleep(unsigned long ms) {
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
ts.tv_sec = ms / 1000ul; /* whole seconds */
|
ts.tv_sec = ms / 1000ul;
|
||||||
ts.tv_nsec = (ms % 1000ul) * 1000; /* remainder, in nanoseconds */
|
ts.tv_nsec = (ms % 1000ul) * 1000;
|
||||||
nanosleep(&ts, NULL);
|
nanosleep(&ts, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* just like strlen */
|
|
||||||
int _strnlen(const char *s, int maxlen) {
|
int _strnlen(const char *s, int maxlen) {
|
||||||
int len = 0;
|
int len = 0;
|
||||||
while (s[len] != 0 && len < maxlen)
|
while (s[len] != 0 && len < maxlen)
|
||||||
@@ -19,7 +17,6 @@ int _strnlen(const char *s, int maxlen) {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unsigned equivalent of strcmp */
|
|
||||||
int ustrcmp(const unsigned char *s1, const unsigned char *s2) {
|
int ustrcmp(const unsigned char *s1, const unsigned char *s2) {
|
||||||
unsigned char c1, c2;
|
unsigned char c1, c2;
|
||||||
|
|
||||||
@@ -34,9 +31,7 @@ int ustrcmp(const unsigned char *s1, const unsigned char *s2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static char *rtp_content_types[64] = {
|
static char *rtp_content_types[64] = {
|
||||||
/* dummy */
|
|
||||||
"DUMMY_CLASS",
|
"DUMMY_CLASS",
|
||||||
/* item */
|
|
||||||
"ITEM.TITLE",
|
"ITEM.TITLE",
|
||||||
"ITEM.ALBUM",
|
"ITEM.ALBUM",
|
||||||
"ITEM.TRACKNUMBER",
|
"ITEM.TRACKNUMBER",
|
||||||
@@ -48,7 +43,6 @@ static char *rtp_content_types[64] = {
|
|||||||
"ITEM.BAND",
|
"ITEM.BAND",
|
||||||
"ITEM.COMMENT",
|
"ITEM.COMMENT",
|
||||||
"ITEM.GENRE",
|
"ITEM.GENRE",
|
||||||
/* info */
|
|
||||||
"INFO.NEWS",
|
"INFO.NEWS",
|
||||||
"INFO.NEWS.LOCAL",
|
"INFO.NEWS.LOCAL",
|
||||||
"INFO.STOCKMARKET",
|
"INFO.STOCKMARKET",
|
||||||
@@ -68,7 +62,6 @@ static char *rtp_content_types[64] = {
|
|||||||
"INFO.ADVERTISEMENT",
|
"INFO.ADVERTISEMENT",
|
||||||
"INFO.URL",
|
"INFO.URL",
|
||||||
"INFO.OTHER",
|
"INFO.OTHER",
|
||||||
/* program */
|
|
||||||
"STATIONNAME.SHORT",
|
"STATIONNAME.SHORT",
|
||||||
"STATIONNAME.LONG",
|
"STATIONNAME.LONG",
|
||||||
"PROGRAMME.NOW",
|
"PROGRAMME.NOW",
|
||||||
@@ -79,7 +72,6 @@ static char *rtp_content_types[64] = {
|
|||||||
"PROGRAMME.FREQUENCY",
|
"PROGRAMME.FREQUENCY",
|
||||||
"PROGRAMME.HOMEPAGE",
|
"PROGRAMME.HOMEPAGE",
|
||||||
"PROGRAMME.SUBCHANNEL",
|
"PROGRAMME.SUBCHANNEL",
|
||||||
/* interactivity */
|
|
||||||
"PHONE.HOTLINE",
|
"PHONE.HOTLINE",
|
||||||
"PHONE.STUDIO",
|
"PHONE.STUDIO",
|
||||||
"PHONE.OTHER",
|
"PHONE.OTHER",
|
||||||
@@ -93,14 +85,11 @@ static char *rtp_content_types[64] = {
|
|||||||
"CHAT.CENTRE",
|
"CHAT.CENTRE",
|
||||||
"VOTE.QUESTION",
|
"VOTE.QUESTION",
|
||||||
"VOTE.CENTRE",
|
"VOTE.CENTRE",
|
||||||
/* rfu */
|
|
||||||
"RFU_1",
|
"RFU_1",
|
||||||
"RFU_2",
|
"RFU_2",
|
||||||
/* private classes */
|
|
||||||
"PRIVATE_1",
|
"PRIVATE_1",
|
||||||
"PRIVATE_2",
|
"PRIVATE_2",
|
||||||
"PRIVATE_3",
|
"PRIVATE_3",
|
||||||
/* descriptor */
|
|
||||||
"PLACE",
|
"PLACE",
|
||||||
"APPOINTMENT",
|
"APPOINTMENT",
|
||||||
"IDENTIFIER",
|
"IDENTIFIER",
|
||||||
@@ -132,7 +121,6 @@ static uint16_t offset_words[] = {
|
|||||||
0x350 /* C' */
|
0x350 /* C' */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Calculate the checkword for each block and emit the bits */
|
|
||||||
void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
||||||
{
|
{
|
||||||
uint8_t i, j, bit, msb;
|
uint8_t i, j, bit, msb;
|
||||||
@@ -142,7 +130,6 @@ void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
|||||||
group_type_b = true;
|
group_type_b = true;
|
||||||
|
|
||||||
for (i = 0; i < GROUP_LENGTH; i++) {
|
for (i = 0; i < GROUP_LENGTH; i++) {
|
||||||
/* Group version B needs C' for block 3 */
|
|
||||||
if (i == 2 && group_type_b) {
|
if (i == 2 && group_type_b) {
|
||||||
offset_word = offset_words[4];
|
offset_word = offset_words[4];
|
||||||
} else {
|
} else {
|
||||||
@@ -151,10 +138,9 @@ void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
|||||||
|
|
||||||
block = blocks[i];
|
block = blocks[i];
|
||||||
|
|
||||||
/* Classical CRC computation */
|
|
||||||
block_crc = 0;
|
block_crc = 0;
|
||||||
for (j = 0; j < BLOCK_SIZE; j++) {
|
for (j = 0; j < BLOCK_SIZE; j++) {
|
||||||
bit = (block & (INT16_15 >> j)) != 0;
|
bit = (block & (0x8000 >> j)) != 0;
|
||||||
msb = (block_crc >> (POLY_DEG - 1)) & 1;
|
msb = (block_crc >> (POLY_DEG - 1)) & 1;
|
||||||
block_crc <<= 1;
|
block_crc <<= 1;
|
||||||
if (msb ^ bit) block_crc ^= POLY;
|
if (msb ^ bit) block_crc ^= POLY;
|
||||||
@@ -167,40 +153,28 @@ void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* AF stuff
|
|
||||||
*/
|
|
||||||
uint8_t add_rds_af(RDSAFs *af_list, float freq) {
|
uint8_t add_rds_af(RDSAFs *af_list, float freq) {
|
||||||
uint16_t af;
|
uint16_t af;
|
||||||
uint8_t entries_reqd = 1; /* default for FM */
|
uint8_t entries_reqd = 1;
|
||||||
|
|
||||||
/*
|
if (freq < 87.6f || freq > 107.9f) {
|
||||||
* check how many slots are required for the frequency
|
|
||||||
*
|
|
||||||
* LF/MF (AM) needs 2 entries: one for the
|
|
||||||
* AM follows code and the freq code itself
|
|
||||||
*/
|
|
||||||
if (freq < 87.6f || freq > 107.9f) { /* is LF/MF */
|
|
||||||
entries_reqd = 2;
|
entries_reqd = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if the AF list is full */
|
|
||||||
if (af_list->num_afs + entries_reqd > MAX_AFS) {
|
if (af_list->num_afs + entries_reqd > MAX_AFS) {
|
||||||
/* Too many AF entries */
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if new frequency is valid */
|
if (freq >= 87.6f && freq <= 107.9f) {
|
||||||
if (freq >= 87.6f && freq <= 107.9f) { /* FM */
|
|
||||||
af = (uint16_t)(freq * 10.0f) - 875;
|
af = (uint16_t)(freq * 10.0f) - 875;
|
||||||
af_list->afs[af_list->num_entries] = af;
|
af_list->afs[af_list->num_entries] = af;
|
||||||
af_list->num_entries += 1;
|
af_list->num_entries += 1;
|
||||||
} else if (freq >= 153.0f && freq <= 279.0f) { /* LF */
|
} else if (freq >= 153.0f && freq <= 279.0f) {
|
||||||
af = (uint16_t)(freq - 153.0f) / 9 + 1;
|
af = (uint16_t)(freq - 153.0f) / 9 + 1;
|
||||||
af_list->afs[af_list->num_entries + 0] = AF_CODE_LFMF_FOLLOWS;
|
af_list->afs[af_list->num_entries + 0] = AF_CODE_LFMF_FOLLOWS;
|
||||||
af_list->afs[af_list->num_entries + 1] = af;
|
af_list->afs[af_list->num_entries + 1] = af;
|
||||||
af_list->num_entries += 2;
|
af_list->num_entries += 2;
|
||||||
} else if (freq >= 531.0f && freq <= 1602.0f) { /* AM 9 kHz spacing */
|
} else if (freq >= 531.0f && freq <= 1602.0f) {
|
||||||
af = (uint16_t)(freq - 531.0f) / 9 + 16;
|
af = (uint16_t)(freq - 531.0f) / 9 + 16;
|
||||||
af_list->afs[af_list->num_entries + 0] = AF_CODE_LFMF_FOLLOWS;
|
af_list->afs[af_list->num_entries + 0] = AF_CODE_LFMF_FOLLOWS;
|
||||||
af_list->afs[af_list->num_entries + 1] = af;
|
af_list->afs[af_list->num_entries + 1] = af;
|
||||||
@@ -214,13 +188,6 @@ uint8_t add_rds_af(RDSAFs *af_list, float freq) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* UTF-8 to RDS char set converter
|
|
||||||
*
|
|
||||||
* Translates certain chars into their RDS equivalents
|
|
||||||
* NOTE!! Only applies to PS, RT and PTYN. LPS uses UTF-8 (SCB = 1)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define XLATSTRLEN 255
|
#define XLATSTRLEN 255
|
||||||
unsigned char *xlat(unsigned char *str) {
|
unsigned char *xlat(unsigned char *str) {
|
||||||
static unsigned char new_str[XLATSTRLEN];
|
static unsigned char new_str[XLATSTRLEN];
|
||||||
|
|||||||
@@ -2,6 +2,61 @@
|
|||||||
|
|
||||||
static float waveform[2][FILTER_SIZE];
|
static float waveform[2][FILTER_SIZE];
|
||||||
|
|
||||||
|
void Modulator_saveToFile(RDSModulatorParameters *emp, const char *option) {
|
||||||
|
char encoderPath[256];
|
||||||
|
snprintf(encoderPath, sizeof(encoderPath), "%s/.rdsEncoder", getenv("HOME"));
|
||||||
|
FILE *file;
|
||||||
|
|
||||||
|
RDSModulatorParameters tempEncoder;
|
||||||
|
file = fopen(encoderPath, "rb");
|
||||||
|
if (file != NULL) {
|
||||||
|
fread(&tempEncoder, sizeof(RDSModulatorParameters), 1, file);
|
||||||
|
fclose(file);
|
||||||
|
} else {
|
||||||
|
memcpy(&tempEncoder, emp, sizeof(RDSModulatorParameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(option, "LEVL") == 0) {
|
||||||
|
tempEncoder.level = emp->level;
|
||||||
|
} else if (strcmp(option, "RDSGEN") == 0) {
|
||||||
|
tempEncoder.rdsgen = emp->rdsgen;
|
||||||
|
} else if (strcmp(option, "ALL") == 0) {
|
||||||
|
tempEncoder.level = emp->level;
|
||||||
|
tempEncoder.rdsgen = emp->rdsgen;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = fopen(encoderPath, "wb");
|
||||||
|
if (file == NULL) {
|
||||||
|
perror("Error opening file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fwrite(&tempEncoder, sizeof(RDSModulatorParameters), 1, file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Modulator_loadFromFile(RDSModulatorParameters *emp) {
|
||||||
|
char encoderPath[256];
|
||||||
|
snprintf(encoderPath, sizeof(encoderPath), "%s/.rdsModulator", getenv("HOME"));
|
||||||
|
FILE *file = fopen(encoderPath, "rb");
|
||||||
|
if (file == NULL) {
|
||||||
|
perror("Error opening file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fread(emp, sizeof(RDSModulatorParameters), 1, file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
int modulatorsaved() {
|
||||||
|
char encoderPath[256];
|
||||||
|
snprintf(encoderPath, sizeof(encoderPath), "%s/.rdsModulator", getenv("HOME"));
|
||||||
|
FILE *file = fopen(encoderPath, "rb");
|
||||||
|
if (file) {
|
||||||
|
fclose(file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc) {
|
void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc) {
|
||||||
memset(rdsMod, 0, sizeof(*rdsMod));
|
memset(rdsMod, 0, sizeof(*rdsMod));
|
||||||
rdsMod->level = 1.0f;
|
rdsMod->level = 1.0f;
|
||||||
@@ -15,6 +70,12 @@ void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc) {
|
|||||||
+waveform_biphase[j] : -waveform_biphase[j];
|
+waveform_biphase[j] : -waveform_biphase[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(modulatorsaved()) {
|
||||||
|
Modulator_loadFromFile(rdsMod->params);
|
||||||
|
} else {
|
||||||
|
Modulator_saveToFile(rdsMod->params, "ALL");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float get_rds_sample(RDSModulator* rdsMod) {
|
float get_rds_sample(RDSModulator* rdsMod) {
|
||||||
@@ -51,5 +112,5 @@ float get_rds_sample(RDSModulator* rdsMod) {
|
|||||||
rdsMod->sample_buffer[rdsMod->out_sample_index++] = 0;
|
rdsMod->sample_buffer[rdsMod->out_sample_index++] = 0;
|
||||||
if (rdsMod->out_sample_index == SAMPLE_BUFFER_SIZE)
|
if (rdsMod->out_sample_index == SAMPLE_BUFFER_SIZE)
|
||||||
rdsMod->out_sample_index = 0;
|
rdsMod->out_sample_index = 0;
|
||||||
return sample*rdsMod->level*rdsMod->rdsgen;
|
return sample*rdsMod->params.level*rdsMod->params.rdsgen;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
#include "rds.h"
|
#include "rds.h"
|
||||||
#include "waveforms.h"
|
#include "waveforms.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
float level;
|
||||||
|
uint8_t rdsgen;
|
||||||
|
} RDSModulatorParameters;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t bit_buffer[BITS_PER_GROUP];
|
uint8_t bit_buffer[BITS_PER_GROUP];
|
||||||
uint8_t bit_pos;
|
uint8_t bit_pos;
|
||||||
@@ -13,10 +19,12 @@ typedef struct {
|
|||||||
uint8_t sample_count;
|
uint8_t sample_count;
|
||||||
uint16_t in_sample_index;
|
uint16_t in_sample_index;
|
||||||
uint16_t out_sample_index;
|
uint16_t out_sample_index;
|
||||||
float level;
|
RDSModulatorParameters params;
|
||||||
uint8_t rdsgen;
|
|
||||||
RDSEncoder* enc;
|
RDSEncoder* enc;
|
||||||
} RDSModulator;
|
} RDSModulator;
|
||||||
|
|
||||||
|
void Modulator_saveToFile(RDSModulatorParameters *emp, const char *option);
|
||||||
|
void Modulator_loadFromFile(RDSModulatorParameters *emp);
|
||||||
|
int modulatorsaved();
|
||||||
void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc);
|
void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc);
|
||||||
float get_rds_sample(RDSModulator* rdsMod);
|
float get_rds_sample(RDSModulator* rdsMod);
|
||||||
|
|||||||
72
src/rds.c
72
src/rds.c
@@ -4,8 +4,6 @@
|
|||||||
#include "lib.h"
|
#include "lib.h"
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#define r_memcpy(src, dst, size) memcpy(dst, src, size)
|
|
||||||
|
|
||||||
void saveToFile(RDSEncoder *emp, const char *option) {
|
void saveToFile(RDSEncoder *emp, const char *option) {
|
||||||
char encoderPath[256];
|
char encoderPath[256];
|
||||||
snprintf(encoderPath, sizeof(encoderPath), "%s/.rdsEncoder", getenv("HOME"));
|
snprintf(encoderPath, sizeof(encoderPath), "%s/.rdsEncoder", getenv("HOME"));
|
||||||
@@ -69,23 +67,38 @@ void saveToFile(RDSEncoder *emp, const char *option) {
|
|||||||
memcpy(tempEncoder.data[emp->program].pin, emp->data[emp->program].pin, sizeof(emp->data[emp->program].pin));
|
memcpy(tempEncoder.data[emp->program].pin, emp->data[emp->program].pin, sizeof(emp->data[emp->program].pin));
|
||||||
} else if (strcmp(option, "GRPSEQ") == 0) {
|
} else if (strcmp(option, "GRPSEQ") == 0) {
|
||||||
memcpy(tempEncoder.data[emp->program].grp_sqc, emp->data[emp->program].grp_sqc, sizeof(emp->data[emp->program].grp_sqc));
|
memcpy(tempEncoder.data[emp->program].grp_sqc, emp->data[emp->program].grp_sqc, sizeof(emp->data[emp->program].grp_sqc));
|
||||||
} else if (strcmp(option, "RTP") == 0 || strcmp(option, "RTPRUN") == 0) {
|
} else if (strcmp(option, "RTP") == 0) {
|
||||||
memcpy(&(tempEncoder.rtpData[emp->program]), &(emp->rtpData[emp->program]), sizeof(emp->rtpData[emp->program]));
|
tempEncoder.rtpData[emp->program].group = emp->rtpData[emp->program].group;
|
||||||
|
memcpy(tempEncoder.rtpData[emp->program].len, emp->rtpData[emp->program].len, sizeof(emp->rtpData[emp->program].len));
|
||||||
|
memcpy(tempEncoder.rtpData[emp->program].start, emp->rtpData[emp->program].start, sizeof(emp->rtpData[emp->program].start));
|
||||||
|
memcpy(tempEncoder.rtpData[emp->program].type, emp->rtpData[emp->program].type, sizeof(emp->rtpData[emp->program].type));
|
||||||
|
tempEncoder.rtpData[emp->program].toggle = emp->rtpData[emp->program].toggle;
|
||||||
} else if (strcmp(option, "UDG1") == 0) {
|
} else if (strcmp(option, "UDG1") == 0) {
|
||||||
memcpy(tempEncoder.data[emp->program].udg1, emp->data[emp->program].udg1, sizeof(emp->data[emp->program].udg1));
|
memcpy(tempEncoder.data[emp->program].udg1, emp->data[emp->program].udg1, sizeof(emp->data[emp->program].udg1));
|
||||||
tempEncoder.data[emp->program].udg1_len = emp->data[emp->program].udg1_len;
|
tempEncoder.data[emp->program].udg1_len = emp->data[emp->program].udg1_len;
|
||||||
} else if (strcmp(option, "UDG2") == 0) {
|
} else if (strcmp(option, "UDG2") == 0) {
|
||||||
memcpy(tempEncoder.data[emp->program].udg2, emp->data[emp->program].udg2, sizeof(emp->data[emp->program].udg2));
|
memcpy(tempEncoder.data[emp->program].udg2, emp->data[emp->program].udg2, sizeof(emp->data[emp->program].udg2));
|
||||||
tempEncoder.data[emp->program].udg2_len = emp->data[emp->program].udg2_len;
|
tempEncoder.data[emp->program].udg2_len = emp->data[emp->program].udg2_len;
|
||||||
|
} else if(strcmp(option, "RTPRUN") == 0) {
|
||||||
|
tempEncoder.rtpData[emp->program].running = emp->rtpData[emp->program].running;
|
||||||
|
tempEncoder.rtpData[emp->program].enabled = emp->rtpData[emp->program].enabled;
|
||||||
|
} else if(strcmp(option, "PTYNEN") == 0) {
|
||||||
|
tempEncoder.data[emp->program].ptyn_enabled = emp->data[emp->program].ptyn_enabled;
|
||||||
|
} else if(strcmp(option, "ECCEN") == 0) {
|
||||||
|
tempEncoder.data[emp->program].ecclic_enabled = emp->data[emp->program].ecclic_enabled;
|
||||||
|
} else if(strcmp(option, "RT1EN") == 0) {
|
||||||
|
tempEncoder.data[emp->program].rt1_enabled = emp->data[emp->program].rt1_enabled;
|
||||||
|
} else if(strcmp(option, "PINEN") == 0) {
|
||||||
|
tempEncoder.data[emp->program].pin[0] = emp->data[emp->program].pin[0];
|
||||||
} else if (strcmp(option, "ALL") == 0) {
|
} else if (strcmp(option, "ALL") == 0) {
|
||||||
r_memcpy(&(emp->data[emp->program]), &(tempEncoder.data[emp->program]), sizeof(RDSData));
|
memcpy(&(tempEncoder.data[emp->program]), &(emp->data[emp->program]), sizeof(RDSData));
|
||||||
r_memcpy(&(emp->rtpData[emp->program]), &(tempEncoder.rtpData[emp->program]), sizeof(RDSRTPlusData));
|
memcpy(&(tempEncoder.rtpData[emp->program]), &(emp->rtpData[emp->program]), sizeof(RDSRTPlusData));
|
||||||
tempEncoder.program = emp->program;
|
tempEncoder.program = emp->program;
|
||||||
}
|
}
|
||||||
|
|
||||||
r_memcpy(&(emp->state[emp->program]), &(tempEncoder.state[emp->program]), sizeof(RDSState));
|
memcpy(&(tempEncoder.state[emp->program]), &(emp->state[emp->program]), sizeof(RDSState));
|
||||||
r_memcpy(&(emp->oda_state[emp->program]), &(tempEncoder.oda_state[emp->program]), sizeof(RDSODAState));
|
memcpy(&(tempEncoder.oda_state[emp->program]), &(emp->oda_state[emp->program]), sizeof(RDSODAState));
|
||||||
r_memcpy(&(emp->odas[emp->program]), &(tempEncoder.odas[emp->program]), sizeof(RDSODA)*MAX_ODAS);
|
memcpy(&(tempEncoder.odas[emp->program]), &(emp->odas[emp->program]), sizeof(RDSODA)*MAX_ODAS);
|
||||||
|
|
||||||
file = fopen(encoderPath, "wb");
|
file = fopen(encoderPath, "wb");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
@@ -173,7 +186,7 @@ static void get_rds_ps_group(RDSEncoder* enc, uint16_t *blocks) {
|
|||||||
if(enc->data[enc->program].ta && enc->state[enc->program].tps_text[0] != '\0') {
|
if(enc->data[enc->program].ta && enc->state[enc->program].tps_text[0] != '\0') {
|
||||||
blocks[3] = enc->state[enc->program].tps_text[enc->state[enc->program].ps_csegment * 2] << 8 | enc->state[enc->program].tps_text[enc->state[enc->program].ps_csegment * 2 + 1];
|
blocks[3] = enc->state[enc->program].tps_text[enc->state[enc->program].ps_csegment * 2] << 8 | enc->state[enc->program].tps_text[enc->state[enc->program].ps_csegment * 2 + 1];
|
||||||
} else {
|
} else {
|
||||||
/* TODO: Add DPS */
|
// TODO: Add DPS
|
||||||
blocks[3] = enc->state[enc->program].ps_text[enc->state[enc->program].ps_csegment * 2] << 8 | enc->state[enc->program].ps_text[enc->state[enc->program].ps_csegment * 2 + 1];
|
blocks[3] = enc->state[enc->program].ps_text[enc->state[enc->program].ps_csegment * 2] << 8 | enc->state[enc->program].ps_text[enc->state[enc->program].ps_csegment * 2 + 1];
|
||||||
}
|
}
|
||||||
enc->state[enc->program].ps_csegment++;
|
enc->state[enc->program].ps_csegment++;
|
||||||
@@ -235,7 +248,7 @@ static uint8_t get_rds_ct_group(RDSEncoder* enc, uint16_t *blocks) {
|
|||||||
|
|
||||||
blocks[1] |= 4 << 12 | (mjd >> 15);
|
blocks[1] |= 4 << 12 | (mjd >> 15);
|
||||||
blocks[2] = (mjd << 1) | (utc->tm_hour >> 4);
|
blocks[2] = (mjd << 1) | (utc->tm_hour >> 4);
|
||||||
blocks[3] = (utc->tm_hour & INT16_L4) << 12 | utc->tm_min << 6;
|
blocks[3] = (utc->tm_hour & 0xf) << 12 | utc->tm_min << 6;
|
||||||
|
|
||||||
local_time = localtime(&now);
|
local_time = localtime(&now);
|
||||||
|
|
||||||
@@ -288,16 +301,15 @@ static void get_rds_ecc_group(RDSEncoder* enc, uint16_t *blocks) {
|
|||||||
blocks[2] = enc->data[enc->program].ecc;
|
blocks[2] = enc->data[enc->program].ecc;
|
||||||
|
|
||||||
if(enc->data[enc->program].pin[0]) {
|
if(enc->data[enc->program].pin[0]) {
|
||||||
blocks[3] = enc->data[enc->program].pin[1] << 11; // day
|
blocks[3] = enc->data[enc->program].pin[1] << 11;
|
||||||
blocks[3] |= enc->data[enc->program].pin[2] << 6; // hour
|
blocks[3] |= enc->data[enc->program].pin[2] << 6;
|
||||||
blocks[3] |= enc->data[enc->program].pin[3]; // minute
|
blocks[3] |= enc->data[enc->program].pin[3];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_rds_lic_group(RDSEncoder* enc, uint16_t *blocks) {
|
static void get_rds_lic_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||||
blocks[1] |= 1 << 12;
|
blocks[1] |= 1 << 12;
|
||||||
blocks[2] = 0x3000; // 0b0011000000000000
|
blocks[2] = 0x3000 | enc->data[enc->program].lic;
|
||||||
blocks[2] |= enc->data[enc->program].lic;
|
|
||||||
|
|
||||||
if(enc->data[enc->program].pin[0]) {
|
if(enc->data[enc->program].pin[0]) {
|
||||||
blocks[3] = enc->data[enc->program].pin[1] << 11; // day
|
blocks[3] = enc->data[enc->program].pin[1] << 11; // day
|
||||||
@@ -309,16 +321,16 @@ 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_TYPE(enc->rtpData[enc->program].group) << 12;
|
||||||
blocks[1] |= GET_GROUP_VER(enc->rtpData[enc->program].group) << 11;
|
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].toggle << 4 | enc->rtpData[enc->program].running << 3;
|
||||||
blocks[1] |= (enc->rtpData[enc->program].type[0] & INT8_U5) >> 3;
|
blocks[1] |= (enc->rtpData[enc->program].type[0] & 0xf8) >> 3;
|
||||||
|
|
||||||
blocks[2] = (enc->rtpData[enc->program].type[0] & INT8_L3) << 13;
|
blocks[2] = (enc->rtpData[enc->program].type[0] & 0x07) << 13;
|
||||||
blocks[2] |= (enc->rtpData[enc->program].start[0] & INT8_L6) << 7;
|
blocks[2] |= (enc->rtpData[enc->program].start[0] & 0x3f) << 7;
|
||||||
blocks[2] |= (enc->rtpData[enc->program].len[0] & INT8_L6) << 1;
|
blocks[2] |= (enc->rtpData[enc->program].len[0] & 0x3f) << 1;
|
||||||
blocks[2] |= (enc->rtpData[enc->program].type[1] & INT8_U3) >> 5;
|
blocks[2] |= (enc->rtpData[enc->program].type[1] & 0xe0) >> 5;
|
||||||
|
|
||||||
blocks[3] = (enc->rtpData[enc->program].type[1] & INT8_L5) << 11;
|
blocks[3] = (enc->rtpData[enc->program].type[1] & 0x1f) << 11;
|
||||||
blocks[3] |= (enc->rtpData[enc->program].start[1] & INT8_L6) << 5;
|
blocks[3] |= (enc->rtpData[enc->program].start[1] & 0x3f) << 5;
|
||||||
blocks[3] |= enc->rtpData[enc->program].len[1] & INT8_L5;
|
blocks[3] |= enc->rtpData[enc->program].len[1] & 0x1f;
|
||||||
}
|
}
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
@@ -380,7 +392,7 @@ static void get_rds_group(RDSEncoder* enc, uint16_t *blocks) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t idx;
|
uint8_t udg_idx;
|
||||||
switch (grp)
|
switch (grp)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
@@ -412,14 +424,14 @@ static void get_rds_group(RDSEncoder* enc, uint16_t *blocks) {
|
|||||||
goto group_coded;
|
goto group_coded;
|
||||||
// TODO: Add EON
|
// TODO: Add EON
|
||||||
case 'X':
|
case 'X':
|
||||||
idx = enc->state[enc->program].udg_idxs[0];
|
udg_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];
|
for(int i = 0; i < 3; i++) blocks[i+1] = enc->data[enc->program].udg1[udg_idx][i];
|
||||||
enc->state[enc->program].udg_idxs[0]++;
|
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;
|
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;
|
goto group_coded;
|
||||||
case 'Y':
|
case 'Y':
|
||||||
idx = enc->state[enc->program].udg_idxs[1];
|
udg_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];
|
for(int i = 0; i < 3; i++) blocks[i+1] = enc->data[enc->program].udg2[udg_idx][i];
|
||||||
enc->state[enc->program].udg_idxs[1]++;
|
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;
|
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;
|
goto group_coded;
|
||||||
@@ -572,7 +584,7 @@ void set_rds_lps(RDSEncoder* enc, unsigned char *lps) {
|
|||||||
|
|
||||||
void set_rds_rtplus_flags(RDSEncoder* enc, uint8_t flags) {
|
void set_rds_rtplus_flags(RDSEncoder* enc, uint8_t flags) {
|
||||||
enc->rtpData[enc->program].enabled = (flags==2);
|
enc->rtpData[enc->program].enabled = (flags==2);
|
||||||
enc->rtpData[enc->program].running = flags & INT8_0;
|
enc->rtpData[enc->program].running = flags & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_rds_rtplus_tags(RDSEncoder* enc, uint8_t *tags) {
|
void set_rds_rtplus_tags(RDSEncoder* enc, uint8_t *tags) {
|
||||||
|
|||||||
92
src/rds.h
92
src/rds.h
@@ -77,7 +77,6 @@ typedef struct {
|
|||||||
|
|
||||||
unsigned char lps[LPS_LENGTH];
|
unsigned char lps[LPS_LENGTH];
|
||||||
|
|
||||||
// Enabled, day, hour, minute
|
|
||||||
uint8_t pin[4];
|
uint8_t pin[4];
|
||||||
|
|
||||||
unsigned char grp_sqc[24];
|
unsigned char grp_sqc[24];
|
||||||
@@ -207,94 +206,7 @@ typedef struct
|
|||||||
#define GROUP_15B (GROUP_TYPE_15 | GROUP_VER_B)
|
#define GROUP_15B (GROUP_TYPE_15 | GROUP_VER_B)
|
||||||
|
|
||||||
#define GET_GROUP_TYPE(x) ((x >> 4) & 15)
|
#define GET_GROUP_TYPE(x) ((x >> 4) & 15)
|
||||||
#define GET_GROUP_VER(x) (x & 1) /* only check bit 0 */
|
#define GET_GROUP_VER(x) (x & 1)
|
||||||
|
|
||||||
#define DI_STEREO (1 << 0)
|
|
||||||
#define DI_AH (1 << 1)
|
|
||||||
#define DI_COMPRESSED (1 << 2)
|
|
||||||
#define DI_DPTY (1 << 3)
|
|
||||||
|
|
||||||
/* Bit mask */
|
|
||||||
|
|
||||||
/* 8 bit */
|
|
||||||
#define INT8_ALL 0xff
|
|
||||||
/* Lower */
|
|
||||||
#define INT8_L1 0x01
|
|
||||||
#define INT8_L2 0x03
|
|
||||||
#define INT8_L3 0x07
|
|
||||||
#define INT8_L4 0x0f
|
|
||||||
#define INT8_L5 0x1f
|
|
||||||
#define INT8_L6 0x3f
|
|
||||||
#define INT8_L7 0x7f
|
|
||||||
/* Upper */
|
|
||||||
#define INT8_U7 0xfe
|
|
||||||
#define INT8_U6 0xfc
|
|
||||||
#define INT8_U5 0xf8
|
|
||||||
#define INT8_U4 0xf0
|
|
||||||
#define INT8_U3 0xe0
|
|
||||||
#define INT8_U2 0xc0
|
|
||||||
#define INT8_U1 0x80
|
|
||||||
/* Single */
|
|
||||||
#define INT8_0 0x01
|
|
||||||
#define INT8_1 0x02
|
|
||||||
#define INT8_2 0x04
|
|
||||||
#define INT8_3 0x08
|
|
||||||
#define INT8_4 0x10
|
|
||||||
#define INT8_5 0x20
|
|
||||||
#define INT8_6 0x40
|
|
||||||
#define INT8_7 0x80
|
|
||||||
|
|
||||||
/* 16 bit */
|
|
||||||
#define INT16_ALL 0xffff
|
|
||||||
/* Lower */
|
|
||||||
#define INT16_L1 0x0001
|
|
||||||
#define INT16_L2 0x0003
|
|
||||||
#define INT16_L3 0x0007
|
|
||||||
#define INT16_L4 0x000f
|
|
||||||
#define INT16_L5 0x001f
|
|
||||||
#define INT16_L6 0x003f
|
|
||||||
#define INT16_L7 0x007f
|
|
||||||
#define INT16_L8 0x00ff
|
|
||||||
#define INT16_L9 0x01ff
|
|
||||||
#define INT16_L10 0x03ff
|
|
||||||
#define INT16_L11 0x07ff
|
|
||||||
#define INT16_L12 0x0fff
|
|
||||||
#define INT16_L13 0x1fff
|
|
||||||
#define INT16_L14 0x3fff
|
|
||||||
#define INT16_L15 0x7fff
|
|
||||||
/* Upper */
|
|
||||||
#define INT16_U15 0xfffe
|
|
||||||
#define INT16_U14 0xfffc
|
|
||||||
#define INT16_U13 0xfff8
|
|
||||||
#define INT16_U12 0xfff0
|
|
||||||
#define INT16_U11 0xffe0
|
|
||||||
#define INT16_U10 0xffc0
|
|
||||||
#define INT16_U9 0xff80
|
|
||||||
#define INT16_U8 0xff00
|
|
||||||
#define INT16_U7 0xfe00
|
|
||||||
#define INT16_U6 0xfc00
|
|
||||||
#define INT16_U5 0xf800
|
|
||||||
#define INT16_U4 0xf000
|
|
||||||
#define INT16_U3 0xe000
|
|
||||||
#define INT16_U2 0xc000
|
|
||||||
#define INT16_U1 0x8000
|
|
||||||
/* Single */
|
|
||||||
#define INT16_0 0x0001
|
|
||||||
#define INT16_1 0x0002
|
|
||||||
#define INT16_2 0x0004
|
|
||||||
#define INT16_3 0x0008
|
|
||||||
#define INT16_4 0x0010
|
|
||||||
#define INT16_5 0x0020
|
|
||||||
#define INT16_6 0x0040
|
|
||||||
#define INT16_7 0x0080
|
|
||||||
#define INT16_8 0x0100
|
|
||||||
#define INT16_9 0x0200
|
|
||||||
#define INT16_10 0x0400
|
|
||||||
#define INT16_11 0x0800
|
|
||||||
#define INT16_12 0x1000
|
|
||||||
#define INT16_13 0x2000
|
|
||||||
#define INT16_14 0x4000
|
|
||||||
#define INT16_15 0x8000
|
|
||||||
|
|
||||||
#define IS_TYPE_B(a) (a[1] & INT16_11)
|
#define IS_TYPE_B(a) (a[1] & INT16_11)
|
||||||
|
|
||||||
@@ -313,4 +225,4 @@ void set_rds_rtplus_flags(RDSEncoder* enc, uint8_t flags);
|
|||||||
void set_rds_rtplus_tags(RDSEncoder* enc, uint8_t *tags);
|
void set_rds_rtplus_tags(RDSEncoder* enc, uint8_t *tags);
|
||||||
void set_rds_ptyn(RDSEncoder* enc, unsigned char *ptyn);
|
void set_rds_ptyn(RDSEncoder* enc, unsigned char *ptyn);
|
||||||
|
|
||||||
#endif /* RDS_H */
|
#endif
|
||||||
|
|||||||
11
src/rds95.c
11
src/rds95.c
@@ -20,7 +20,6 @@ static void stop() {
|
|||||||
stop_rds = 1;
|
stop_rds = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* threads */
|
|
||||||
static void *control_pipe_worker(void* modulator) {
|
static void *control_pipe_worker(void* modulator) {
|
||||||
RDSModulator *mod = (RDSModulator*)modulator;
|
RDSModulator *mod = (RDSModulator*)modulator;
|
||||||
while (!stop_rds) {
|
while (!stop_rds) {
|
||||||
@@ -48,11 +47,10 @@ static void show_help(char *name) {
|
|||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
char control_pipe[51] = "\0";
|
char control_pipe[51] = "\0";
|
||||||
/* PASIMPLE */
|
|
||||||
pa_simple *device;
|
pa_simple *device;
|
||||||
pa_sample_spec format;
|
pa_sample_spec format;
|
||||||
|
|
||||||
/* pthread */
|
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
pthread_t control_pipe_thread;
|
pthread_t control_pipe_thread;
|
||||||
|
|
||||||
@@ -69,25 +67,22 @@ int main(int argc, char **argv) {
|
|||||||
int opt;
|
int opt;
|
||||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'C': /* ctl */
|
case 'C':
|
||||||
memcpy(control_pipe, optarg, 50);
|
memcpy(control_pipe, optarg, 50);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'h': /* help */
|
case 'h':
|
||||||
default:
|
default:
|
||||||
show_help(argv[0]);
|
show_help(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize pthread stuff */
|
|
||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
|
|
||||||
/* Gracefully stop the encoder on SIGINT or SIGTERM */
|
|
||||||
signal(SIGINT, stop);
|
signal(SIGINT, stop);
|
||||||
signal(SIGTERM, stop);
|
signal(SIGTERM, stop);
|
||||||
|
|
||||||
/* PASIMPLE format */
|
|
||||||
format.format = PA_SAMPLE_FLOAT32NE;
|
format.format = PA_SAMPLE_FLOAT32NE;
|
||||||
format.channels = 1;
|
format.channels = 1;
|
||||||
format.rate = RDS_SAMPLE_RATE;
|
format.rate = RDS_SAMPLE_RATE;
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
AF - Mode A only
|
|
||||||
AFCH - Mode A only
|
|
||||||
DI
|
|
||||||
MS
|
|
||||||
PI
|
|
||||||
PS
|
|
||||||
TPS
|
|
||||||
PTY
|
|
||||||
PTYN
|
|
||||||
PTYNEN
|
|
||||||
TEXT | RT1
|
|
||||||
RT1EN
|
|
||||||
TA
|
|
||||||
TP
|
|
||||||
CT
|
|
||||||
ECC
|
|
||||||
ECCEN
|
|
||||||
G
|
|
||||||
LIC
|
|
||||||
PIN
|
|
||||||
PINEN
|
|
||||||
RTP
|
|
||||||
RTPRUN
|
|
||||||
|
|
||||||
(Magic RDS 3 'Program' page is fully supported.)
|
|
||||||
26
src/todo.txt
26
src/todo.txt
@@ -1,26 +0,0 @@
|
|||||||
Current TODO for RDS encoding:
|
|
||||||
- UECP Decoding
|
|
||||||
- Dynamic PS (may not be implemented)
|
|
||||||
- Bidirectionality
|
|
||||||
|
|
||||||
ASCII commands to implement:
|
|
||||||
- Group sequence
|
|
||||||
- Short RT
|
|
||||||
|
|
||||||
ASCII Group seqeunce:
|
|
||||||
0 - Four 0As (PS x 4)
|
|
||||||
1 - 1A (ECC/LIC, PIN)
|
|
||||||
2 - 2A (RT)
|
|
||||||
A - 10A (PTYN)
|
|
||||||
E - 14A 14B (EON, yet to implement)
|
|
||||||
X - UDG1 (yet to implement, max 8 groups per udg)
|
|
||||||
Y - UDG2 (yet to implement)
|
|
||||||
R - RT+ 3A 11A (do i switch between them like ecc lic?)
|
|
||||||
F - 15A (LPS)
|
|
||||||
Max 24 items in a sequence.
|
|
||||||
|
|
||||||
Note that most of this is takes out of pira.cz's P164 RDS Encoder's manual: https://pira.cz/rds/p164man.pdf
|
|
||||||
|
|
||||||
I plan for this's commands to be similiar to PIRA32's or P164's (both are made by pira.cz)
|
|
||||||
|
|
||||||
In theory, now you can use the MiniRDS as a PIRA32 type device in Magic RDS 4 (but not everything is supported)
|
|
||||||
81
supported_cmds.txt
Normal file
81
supported_cmds.txt
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
AF
|
||||||
|
AFCH
|
||||||
|
DI
|
||||||
|
- DPS1
|
||||||
|
- DPS1EN
|
||||||
|
- DPS2EN
|
||||||
|
- DPS1ENQ
|
||||||
|
- DPS2
|
||||||
|
- DPS1MOD
|
||||||
|
- DPS2MOD
|
||||||
|
- DPS1REP
|
||||||
|
- DPS2REP
|
||||||
|
- DTTMOUT
|
||||||
|
- EQTEXT1
|
||||||
|
- LABPER
|
||||||
|
MS
|
||||||
|
PI
|
||||||
|
PS
|
||||||
|
PTY
|
||||||
|
PTYN
|
||||||
|
PTYNEN
|
||||||
|
RT1 | TEXT
|
||||||
|
RT1EN
|
||||||
|
- RT2
|
||||||
|
- RT2EN
|
||||||
|
- RTPER
|
||||||
|
- RTTYPE
|
||||||
|
- RSTDPS
|
||||||
|
- SCRLSPD
|
||||||
|
- SPSPER
|
||||||
|
TA
|
||||||
|
- TATMOUT
|
||||||
|
TP
|
||||||
|
TPS
|
||||||
|
INIT
|
||||||
|
ALL
|
||||||
|
- HELP
|
||||||
|
- EON*
|
||||||
|
- MSG*
|
||||||
|
- DPS2MSG
|
||||||
|
- RT2MSG
|
||||||
|
- SLIST
|
||||||
|
- SEN
|
||||||
|
- Sxx*
|
||||||
|
(COMSPD)
|
||||||
|
CT
|
||||||
|
(DATE)
|
||||||
|
- ECHO
|
||||||
|
(EXTSYNC)
|
||||||
|
LEVEL
|
||||||
|
(LTO)
|
||||||
|
(PHASE)
|
||||||
|
(MJD)
|
||||||
|
(PILOT)
|
||||||
|
RDSGEN
|
||||||
|
(RESET)
|
||||||
|
(SPEED)
|
||||||
|
- STATUS, ??
|
||||||
|
(TIME)
|
||||||
|
- VER
|
||||||
|
- ADR
|
||||||
|
- CC
|
||||||
|
- DSN*
|
||||||
|
ECCEN
|
||||||
|
G
|
||||||
|
GRPSEQ
|
||||||
|
LIC
|
||||||
|
- NOHDR
|
||||||
|
PIN
|
||||||
|
PINEN
|
||||||
|
PROGRAM
|
||||||
|
- PSN*
|
||||||
|
(PSW)
|
||||||
|
RTP
|
||||||
|
RTPRUN
|
||||||
|
SHORTRT
|
||||||
|
- SEL
|
||||||
|
- SITE
|
||||||
|
UDG1
|
||||||
|
UDG2
|
||||||
|
- UECP
|
||||||
Reference in New Issue
Block a user