mirror of
https://github.com/radio95-rnt/rds95.git
synced 2026-02-26 20:33:53 +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;
|
||||
} command_handler_t;
|
||||
|
||||
// Command handlers
|
||||
static void handle_ptyn(unsigned char *arg, RDSModulator* mod) {
|
||||
arg[PTYN_LENGTH] = 0;
|
||||
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) {
|
||||
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) {
|
||||
mod->rdsgen = strtoul((char *)arg, NULL, 10);
|
||||
mod->params.rdsgen = strtoul((char *)arg, NULL, 10);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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]);
|
||||
|
||||
if (count != 3) {
|
||||
break; // Couldn't parse a complete set of 3 blocks
|
||||
break;
|
||||
}
|
||||
|
||||
sets++;
|
||||
|
||||
// Look for the comma separator
|
||||
while (*ptr && *ptr != ',') {
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (*ptr == ',') {
|
||||
ptr++; // Skip over the comma
|
||||
ptr++;
|
||||
} 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);
|
||||
}
|
||||
|
||||
// Command tables organized by delimiter position and command length
|
||||
static const command_handler_t commands_eq3[] = {
|
||||
{"MS", handle_ms, 2},
|
||||
{"PS", handle_ps, 2},
|
||||
{"CT", handle_ct, 2},
|
||||
{"DI", handle_di, 2},
|
||||
{"PI", handle_pi, 2},
|
||||
{"TP", handle_tp, 2},
|
||||
{"TA", handle_ta, 2},
|
||||
{"MS", handle_ms, 2},
|
||||
{"PI", handle_pi, 2},
|
||||
{"DI", handle_di, 2},
|
||||
{"CT", handle_ct, 2},
|
||||
{"AF", handle_af, 2}
|
||||
};
|
||||
|
||||
@@ -374,7 +371,6 @@ static const command_handler_t commands_exact[] = {
|
||||
// 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,
|
||||
unsigned char *cmd, unsigned char *arg, RDSModulator* mod) {
|
||||
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};
|
||||
snprintf(option, sizeof(option), "%s", (const char*)str);
|
||||
saveToFile(mod->enc, option);
|
||||
Modulator_saveToFile(mod->params, option);
|
||||
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] == '=') {
|
||||
cmd = str;
|
||||
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] == '=') {
|
||||
cmd = str;
|
||||
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] == '=') {
|
||||
cmd = str;
|
||||
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] == '=') {
|
||||
cmd = str;
|
||||
cmd[5] = 0;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* common includes for rds95 */
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -7,7 +6,6 @@
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* workaround for missing pi definition */
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
@@ -7,40 +7,29 @@
|
||||
static int fd;
|
||||
static struct pollfd poller;
|
||||
|
||||
/*
|
||||
* Opens a file (pipe) to be used to control the RDS coder.
|
||||
*/
|
||||
int open_control_pipe(char *filename) {
|
||||
fd = open(filename, O_RDONLY | O_NONBLOCK);
|
||||
if (fd == -1) return -1;
|
||||
|
||||
/* setup the poller */
|
||||
poller.fd = fd;
|
||||
poller.events = POLLIN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Polls the control file (pipe), and if a command is received,
|
||||
* calls process_ascii_cmd.
|
||||
*/
|
||||
void poll_control_pipe(RDSModulator* mod) {
|
||||
static unsigned char pipe_buf[CTL_BUFFER_SIZE];
|
||||
static unsigned char cmd_buf[CMD_BUFFER_SIZE];
|
||||
int bytes_read;
|
||||
char *token;
|
||||
|
||||
/* check for new commands - return early if none */
|
||||
if (poll(&poller, 1, READ_TIMEOUT_MS) <= 0) return;
|
||||
if (!(poller.revents & POLLIN)) return;
|
||||
|
||||
/* read data from pipe */
|
||||
memset(pipe_buf, 0, CTL_BUFFER_SIZE);
|
||||
bytes_read = read(fd, pipe_buf, CTL_BUFFER_SIZE - 1);
|
||||
|
||||
if (bytes_read <= 0) return;
|
||||
|
||||
/* process each command line */
|
||||
token = strtok((char *)pipe_buf, "\n");
|
||||
while (token != NULL) {
|
||||
size_t cmd_len = strlen(token);
|
||||
|
||||
49
src/lib.c
49
src/lib.c
@@ -3,15 +3,13 @@
|
||||
#include <time.h>
|
||||
|
||||
extern int nanosleep(const struct timespec *req, struct timespec *rem);
|
||||
/* millisecond sleep */
|
||||
void msleep(unsigned long ms) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = ms / 1000ul; /* whole seconds */
|
||||
ts.tv_nsec = (ms % 1000ul) * 1000; /* remainder, in nanoseconds */
|
||||
ts.tv_sec = ms / 1000ul;
|
||||
ts.tv_nsec = (ms % 1000ul) * 1000;
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
/* just like strlen */
|
||||
int _strnlen(const char *s, int maxlen) {
|
||||
int len = 0;
|
||||
while (s[len] != 0 && len < maxlen)
|
||||
@@ -19,7 +17,6 @@ int _strnlen(const char *s, int maxlen) {
|
||||
return len;
|
||||
}
|
||||
|
||||
/* unsigned equivalent of strcmp */
|
||||
int ustrcmp(const unsigned char *s1, const unsigned char *s2) {
|
||||
unsigned char c1, c2;
|
||||
|
||||
@@ -34,9 +31,7 @@ int ustrcmp(const unsigned char *s1, const unsigned char *s2) {
|
||||
}
|
||||
|
||||
static char *rtp_content_types[64] = {
|
||||
/* dummy */
|
||||
"DUMMY_CLASS",
|
||||
/* item */
|
||||
"ITEM.TITLE",
|
||||
"ITEM.ALBUM",
|
||||
"ITEM.TRACKNUMBER",
|
||||
@@ -48,7 +43,6 @@ static char *rtp_content_types[64] = {
|
||||
"ITEM.BAND",
|
||||
"ITEM.COMMENT",
|
||||
"ITEM.GENRE",
|
||||
/* info */
|
||||
"INFO.NEWS",
|
||||
"INFO.NEWS.LOCAL",
|
||||
"INFO.STOCKMARKET",
|
||||
@@ -68,7 +62,6 @@ static char *rtp_content_types[64] = {
|
||||
"INFO.ADVERTISEMENT",
|
||||
"INFO.URL",
|
||||
"INFO.OTHER",
|
||||
/* program */
|
||||
"STATIONNAME.SHORT",
|
||||
"STATIONNAME.LONG",
|
||||
"PROGRAMME.NOW",
|
||||
@@ -79,7 +72,6 @@ static char *rtp_content_types[64] = {
|
||||
"PROGRAMME.FREQUENCY",
|
||||
"PROGRAMME.HOMEPAGE",
|
||||
"PROGRAMME.SUBCHANNEL",
|
||||
/* interactivity */
|
||||
"PHONE.HOTLINE",
|
||||
"PHONE.STUDIO",
|
||||
"PHONE.OTHER",
|
||||
@@ -93,14 +85,11 @@ static char *rtp_content_types[64] = {
|
||||
"CHAT.CENTRE",
|
||||
"VOTE.QUESTION",
|
||||
"VOTE.CENTRE",
|
||||
/* rfu */
|
||||
"RFU_1",
|
||||
"RFU_2",
|
||||
/* private classes */
|
||||
"PRIVATE_1",
|
||||
"PRIVATE_2",
|
||||
"PRIVATE_3",
|
||||
/* descriptor */
|
||||
"PLACE",
|
||||
"APPOINTMENT",
|
||||
"IDENTIFIER",
|
||||
@@ -132,7 +121,6 @@ static uint16_t offset_words[] = {
|
||||
0x350 /* C' */
|
||||
};
|
||||
|
||||
/* Calculate the checkword for each block and emit the bits */
|
||||
void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
||||
{
|
||||
uint8_t i, j, bit, msb;
|
||||
@@ -142,7 +130,6 @@ void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
||||
group_type_b = true;
|
||||
|
||||
for (i = 0; i < GROUP_LENGTH; i++) {
|
||||
/* Group version B needs C' for block 3 */
|
||||
if (i == 2 && group_type_b) {
|
||||
offset_word = offset_words[4];
|
||||
} else {
|
||||
@@ -151,10 +138,9 @@ void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
||||
|
||||
block = blocks[i];
|
||||
|
||||
/* Classical CRC computation */
|
||||
block_crc = 0;
|
||||
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;
|
||||
block_crc <<= 1;
|
||||
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) {
|
||||
uint16_t af;
|
||||
uint8_t entries_reqd = 1; /* default for FM */
|
||||
uint8_t entries_reqd = 1;
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
if (freq < 87.6f || freq > 107.9f) {
|
||||
entries_reqd = 2;
|
||||
}
|
||||
|
||||
/* check if the AF list is full */
|
||||
if (af_list->num_afs + entries_reqd > MAX_AFS) {
|
||||
/* Too many AF entries */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* check if new frequency is valid */
|
||||
if (freq >= 87.6f && freq <= 107.9f) { /* FM */
|
||||
if (freq >= 87.6f && freq <= 107.9f) {
|
||||
af = (uint16_t)(freq * 10.0f) - 875;
|
||||
af_list->afs[af_list->num_entries] = af;
|
||||
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_list->afs[af_list->num_entries + 0] = AF_CODE_LFMF_FOLLOWS;
|
||||
af_list->afs[af_list->num_entries + 1] = af;
|
||||
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_list->afs[af_list->num_entries + 0] = AF_CODE_LFMF_FOLLOWS;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
unsigned char *xlat(unsigned char *str) {
|
||||
static unsigned char new_str[XLATSTRLEN];
|
||||
|
||||
@@ -2,6 +2,61 @@
|
||||
|
||||
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) {
|
||||
memset(rdsMod, 0, sizeof(*rdsMod));
|
||||
rdsMod->level = 1.0f;
|
||||
@@ -15,6 +70,12 @@ void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc) {
|
||||
+waveform_biphase[j] : -waveform_biphase[j];
|
||||
}
|
||||
}
|
||||
|
||||
if(modulatorsaved()) {
|
||||
Modulator_loadFromFile(rdsMod->params);
|
||||
} else {
|
||||
Modulator_saveToFile(rdsMod->params, "ALL");
|
||||
}
|
||||
}
|
||||
|
||||
float get_rds_sample(RDSModulator* rdsMod) {
|
||||
@@ -51,5 +112,5 @@ float get_rds_sample(RDSModulator* rdsMod) {
|
||||
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;
|
||||
return sample*rdsMod->params.level*rdsMod->params.rdsgen;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
#include "rds.h"
|
||||
#include "waveforms.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float level;
|
||||
uint8_t rdsgen;
|
||||
} RDSModulatorParameters;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bit_buffer[BITS_PER_GROUP];
|
||||
uint8_t bit_pos;
|
||||
@@ -13,10 +19,12 @@ typedef struct {
|
||||
uint8_t sample_count;
|
||||
uint16_t in_sample_index;
|
||||
uint16_t out_sample_index;
|
||||
float level;
|
||||
uint8_t rdsgen;
|
||||
RDSModulatorParameters params;
|
||||
RDSEncoder* enc;
|
||||
} RDSModulator;
|
||||
|
||||
void Modulator_saveToFile(RDSModulatorParameters *emp, const char *option);
|
||||
void Modulator_loadFromFile(RDSModulatorParameters *emp);
|
||||
int modulatorsaved();
|
||||
void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc);
|
||||
float get_rds_sample(RDSModulator* rdsMod);
|
||||
|
||||
72
src/rds.c
72
src/rds.c
@@ -4,8 +4,6 @@
|
||||
#include "lib.h"
|
||||
#include <time.h>
|
||||
|
||||
#define r_memcpy(src, dst, size) memcpy(dst, src, size)
|
||||
|
||||
void saveToFile(RDSEncoder *emp, const char *option) {
|
||||
char encoderPath[256];
|
||||
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));
|
||||
} 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));
|
||||
} else if (strcmp(option, "RTP") == 0 || strcmp(option, "RTPRUN") == 0) {
|
||||
memcpy(&(tempEncoder.rtpData[emp->program]), &(emp->rtpData[emp->program]), sizeof(emp->rtpData[emp->program]));
|
||||
} else if (strcmp(option, "RTP") == 0) {
|
||||
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) {
|
||||
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;
|
||||
} else if (strcmp(option, "UDG2") == 0) {
|
||||
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;
|
||||
} 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) {
|
||||
r_memcpy(&(emp->data[emp->program]), &(tempEncoder.data[emp->program]), sizeof(RDSData));
|
||||
r_memcpy(&(emp->rtpData[emp->program]), &(tempEncoder.rtpData[emp->program]), sizeof(RDSRTPlusData));
|
||||
memcpy(&(tempEncoder.data[emp->program]), &(emp->data[emp->program]), sizeof(RDSData));
|
||||
memcpy(&(tempEncoder.rtpData[emp->program]), &(emp->rtpData[emp->program]), sizeof(RDSRTPlusData));
|
||||
tempEncoder.program = emp->program;
|
||||
}
|
||||
|
||||
r_memcpy(&(emp->state[emp->program]), &(tempEncoder.state[emp->program]), sizeof(RDSState));
|
||||
r_memcpy(&(emp->oda_state[emp->program]), &(tempEncoder.oda_state[emp->program]), sizeof(RDSODAState));
|
||||
r_memcpy(&(emp->odas[emp->program]), &(tempEncoder.odas[emp->program]), sizeof(RDSODA)*MAX_ODAS);
|
||||
memcpy(&(tempEncoder.state[emp->program]), &(emp->state[emp->program]), sizeof(RDSState));
|
||||
memcpy(&(tempEncoder.oda_state[emp->program]), &(emp->oda_state[emp->program]), sizeof(RDSODAState));
|
||||
memcpy(&(tempEncoder.odas[emp->program]), &(emp->odas[emp->program]), sizeof(RDSODA)*MAX_ODAS);
|
||||
|
||||
file = fopen(encoderPath, "wb");
|
||||
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') {
|
||||
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 {
|
||||
/* 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];
|
||||
}
|
||||
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[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);
|
||||
|
||||
@@ -288,16 +301,15 @@ static void get_rds_ecc_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
blocks[2] = enc->data[enc->program].ecc;
|
||||
|
||||
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
|
||||
blocks[3] = enc->data[enc->program].pin[1] << 11;
|
||||
blocks[3] |= enc->data[enc->program].pin[2] << 6;
|
||||
blocks[3] |= enc->data[enc->program].pin[3];
|
||||
}
|
||||
}
|
||||
|
||||
static void get_rds_lic_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
blocks[1] |= 1 << 12;
|
||||
blocks[2] = 0x3000; // 0b0011000000000000
|
||||
blocks[2] |= enc->data[enc->program].lic;
|
||||
blocks[2] = 0x3000 | enc->data[enc->program].lic;
|
||||
|
||||
if(enc->data[enc->program].pin[0]) {
|
||||
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_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[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].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[2] = (enc->rtpData[enc->program].type[0] & 0x07) << 13;
|
||||
blocks[2] |= (enc->rtpData[enc->program].start[0] & 0x3f) << 7;
|
||||
blocks[2] |= (enc->rtpData[enc->program].len[0] & 0x3f) << 1;
|
||||
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].start[1] & INT8_L6) << 5;
|
||||
blocks[3] |= enc->rtpData[enc->program].len[1] & INT8_L5;
|
||||
blocks[3] = (enc->rtpData[enc->program].type[1] & 0x1f) << 11;
|
||||
blocks[3] |= (enc->rtpData[enc->program].start[1] & 0x3f) << 5;
|
||||
blocks[3] |= enc->rtpData[enc->program].len[1] & 0x1f;
|
||||
}
|
||||
// #endregion
|
||||
|
||||
@@ -380,7 +392,7 @@ static void get_rds_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t idx;
|
||||
uint8_t udg_idx;
|
||||
switch (grp)
|
||||
{
|
||||
default:
|
||||
@@ -412,14 +424,14 @@ static void get_rds_group(RDSEncoder* enc, uint16_t *blocks) {
|
||||
goto group_coded;
|
||||
// TODO: Add EON
|
||||
case 'X':
|
||||
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];
|
||||
udg_idx = enc->state[enc->program].udg_idxs[0];
|
||||
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]++;
|
||||
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 = enc->state[enc->program].udg_idxs[1];
|
||||
for(int i = 0; i < 3; i++) blocks[i+1] = enc->data[enc->program].udg2[idx][i];
|
||||
udg_idx = enc->state[enc->program].udg_idxs[1];
|
||||
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]++;
|
||||
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;
|
||||
@@ -572,7 +584,7 @@ void set_rds_lps(RDSEncoder* enc, unsigned char *lps) {
|
||||
|
||||
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;
|
||||
enc->rtpData[enc->program].running = flags & 1;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
// Enabled, day, hour, minute
|
||||
uint8_t pin[4];
|
||||
|
||||
unsigned char grp_sqc[24];
|
||||
@@ -207,94 +206,7 @@ typedef struct
|
||||
#define GROUP_15B (GROUP_TYPE_15 | GROUP_VER_B)
|
||||
|
||||
#define GET_GROUP_TYPE(x) ((x >> 4) & 15)
|
||||
#define GET_GROUP_VER(x) (x & 1) /* only check bit 0 */
|
||||
|
||||
#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 GET_GROUP_VER(x) (x & 1)
|
||||
|
||||
#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_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;
|
||||
}
|
||||
|
||||
/* threads */
|
||||
static void *control_pipe_worker(void* modulator) {
|
||||
RDSModulator *mod = (RDSModulator*)modulator;
|
||||
while (!stop_rds) {
|
||||
@@ -48,11 +47,10 @@ static void show_help(char *name) {
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char control_pipe[51] = "\0";
|
||||
/* PASIMPLE */
|
||||
|
||||
pa_simple *device;
|
||||
pa_sample_spec format;
|
||||
|
||||
/* pthread */
|
||||
pthread_attr_t attr;
|
||||
pthread_t control_pipe_thread;
|
||||
|
||||
@@ -69,25 +67,22 @@ int main(int argc, char **argv) {
|
||||
int opt;
|
||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'C': /* ctl */
|
||||
case 'C':
|
||||
memcpy(control_pipe, optarg, 50);
|
||||
break;
|
||||
|
||||
case 'h': /* help */
|
||||
case 'h':
|
||||
default:
|
||||
show_help(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize pthread stuff */
|
||||
pthread_attr_init(&attr);
|
||||
|
||||
/* Gracefully stop the encoder on SIGINT or SIGTERM */
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
|
||||
/* PASIMPLE format */
|
||||
format.format = PA_SAMPLE_FLOAT32NE;
|
||||
format.channels = 1;
|
||||
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)
|
||||
Reference in New Issue
Block a user