From 222d5c7fa9dc1bc45b7141d6e1740627bc548372 Mon Sep 17 00:00:00 2001 From: KubaPro010 Date: Sat, 15 Mar 2025 19:33:11 +0100 Subject: [PATCH] refactor RDS modulator parameters and file handling; remove deprecated commands --- src/ascii_cmd.c | 29 +++++-------- src/common.h | 2 - src/control_pipe.c | 11 ----- src/lib.c | 49 ++++------------------ src/modulator.c | 63 ++++++++++++++++++++++++++++- src/modulator.h | 12 +++++- src/rds.c | 72 +++++++++++++++++++-------------- src/rds.h | 92 +----------------------------------------- src/rds95.c | 11 ++--- src/supported_cmds.txt | 25 ------------ src/todo.txt | 26 ------------ supported_cmds.txt | 81 +++++++++++++++++++++++++++++++++++++ 12 files changed, 219 insertions(+), 254 deletions(-) delete mode 100644 src/supported_cmds.txt delete mode 100644 src/todo.txt create mode 100644 supported_cmds.txt diff --git a/src/ascii_cmd.c b/src/ascii_cmd.c index 052dd3a..04c06e4 100644 --- a/src/ascii_cmd.c +++ b/src/ascii_cmd.c @@ -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; diff --git a/src/common.h b/src/common.h index 01734b8..5faa727 100644 --- a/src/common.h +++ b/src/common.h @@ -1,4 +1,3 @@ -/* common includes for rds95 */ #include #include #include @@ -7,7 +6,6 @@ #include #include -/* workaround for missing pi definition */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif diff --git a/src/control_pipe.c b/src/control_pipe.c index 615a602..1afe5b4 100644 --- a/src/control_pipe.c +++ b/src/control_pipe.c @@ -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); diff --git a/src/lib.c b/src/lib.c index 138d5f6..e9625b9 100644 --- a/src/lib.c +++ b/src/lib.c @@ -3,15 +3,13 @@ #include 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]; diff --git a/src/modulator.c b/src/modulator.c index 85ae8ee..eb14d77 100644 --- a/src/modulator.c +++ b/src/modulator.c @@ -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; } diff --git a/src/modulator.h b/src/modulator.h index 8b2884e..d7133e3 100644 --- a/src/modulator.h +++ b/src/modulator.h @@ -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); diff --git a/src/rds.c b/src/rds.c index 807441e..1b3c0e2 100644 --- a/src/rds.c +++ b/src/rds.c @@ -4,8 +4,6 @@ #include "lib.h" #include -#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) { diff --git a/src/rds.h b/src/rds.h index 7a684f1..41340c4 100644 --- a/src/rds.h +++ b/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 diff --git a/src/rds95.c b/src/rds95.c index 0f99889..db463bc 100644 --- a/src/rds95.c +++ b/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; diff --git a/src/supported_cmds.txt b/src/supported_cmds.txt deleted file mode 100644 index 6f3cf56..0000000 --- a/src/supported_cmds.txt +++ /dev/null @@ -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.) \ No newline at end of file diff --git a/src/todo.txt b/src/todo.txt deleted file mode 100644 index 3accd87..0000000 --- a/src/todo.txt +++ /dev/null @@ -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) \ No newline at end of file diff --git a/supported_cmds.txt b/supported_cmds.txt new file mode 100644 index 0000000..26aece3 --- /dev/null +++ b/supported_cmds.txt @@ -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