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