0
1
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:
2025-03-15 19:33:11 +01:00
parent 0cd72a503a
commit 222d5c7fa9
12 changed files with 219 additions and 254 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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];

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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

View File

@@ -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;

View File

@@ -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.)

View File

@@ -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
View 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