mirror of
https://github.com/radio95-rnt/rds95.git
synced 2026-02-27 12:53:53 +01:00
add doc, remove di, add symbol_shifting
This commit is contained in:
2
.vscode/.server-controller-port.log
vendored
2
.vscode/.server-controller-port.log
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"port": 13452,
|
"port": 13452,
|
||||||
"time": 1742754117510,
|
"time": 1742823978722,
|
||||||
"version": "0.0.3"
|
"version": "0.0.3"
|
||||||
}
|
}
|
||||||
106
doc.md
Normal file
106
doc.md
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# RDS95
|
||||||
|
|
||||||
|
RDS95 is a ligth software RDS encoder for linux
|
||||||
|
|
||||||
|
RDS95 follows the IEC 62106 standard (available at the RDS Forum website)
|
||||||
|
|
||||||
|
## Diffrences between IEC 62106 and EN 50067
|
||||||
|
|
||||||
|
The newer standard which is the IEC one, removes these features:
|
||||||
|
|
||||||
|
- MS
|
||||||
|
- PIN
|
||||||
|
- LIC
|
||||||
|
- DI (partially, only dynamic pty is left)
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### PS
|
||||||
|
|
||||||
|
Sets the Program Service: `PS=* RDS *`
|
||||||
|
|
||||||
|
### PI
|
||||||
|
|
||||||
|
Sets the PI code: `PI=FFFF`
|
||||||
|
|
||||||
|
### TP
|
||||||
|
|
||||||
|
Sets the TP flag: `TP=1`
|
||||||
|
|
||||||
|
### TA
|
||||||
|
|
||||||
|
Sets the TA flag and triggers Traffic PS: `TA=0`
|
||||||
|
*May be overridden by EON*
|
||||||
|
|
||||||
|
### DPTY
|
||||||
|
|
||||||
|
Sets the DPTY flag: `DPTY=1`
|
||||||
|
*Formerly DI*
|
||||||
|
|
||||||
|
### CT
|
||||||
|
|
||||||
|
Toggles the transmission of CT groups: `CT=1`
|
||||||
|
|
||||||
|
### AF
|
||||||
|
|
||||||
|
Sets the AF frequencies: `AF=95,89.1`
|
||||||
|
Clears the AF: `AF=`
|
||||||
|
|
||||||
|
### TPS
|
||||||
|
|
||||||
|
Sets the Traffic PS: `TPS=Traffic!` (default not set)
|
||||||
|
*TPS is transmitted instead of PS when TA flag is on*
|
||||||
|
|
||||||
|
### RT1
|
||||||
|
|
||||||
|
Sets the first radio text: `RT1=Currently Playing: Jessie Ware - Remember Where You Are` or `TEXT=Currently Playing: Jessie Ware - Remember Where You Are`
|
||||||
|
|
||||||
|
### RT2
|
||||||
|
|
||||||
|
Sets the second radio text: `RT2=Radio Nova - Best Hits around!`
|
||||||
|
|
||||||
|
### PTY
|
||||||
|
|
||||||
|
Sets the programme type flag: `PTY=11`
|
||||||
|
|
||||||
|
PTY values are diffrent for RDS and RDBS, look for them online
|
||||||
|
|
||||||
|
### ECC
|
||||||
|
|
||||||
|
Sets the extended country code: `ECC=E2`
|
||||||
|
*Note that the ECC is depended on the first letter of the PI, for example PI:3 and ECC:E2 is poland, but PI:1 would be the czech republic*
|
||||||
|
|
||||||
|
### RTP
|
||||||
|
|
||||||
|
TODO: RTP
|
||||||
|
|
||||||
|
### LPS
|
||||||
|
|
||||||
|
Sets the LPS: `LPS=NovaFM❤️`
|
||||||
|
*Note that LPS does UTF-8, while PS, RT don't*
|
||||||
|
|
||||||
|
### PTYN
|
||||||
|
|
||||||
|
Sets the programme type name: `PTYN=Football`
|
||||||
|
|
||||||
|
### AFCH
|
||||||
|
|
||||||
|
TODO: AFCH
|
||||||
|
|
||||||
|
### UDG1
|
||||||
|
|
||||||
|
Sets the user defined group, max 8 groups: `UDG1=6000FFFFFFFF`
|
||||||
|
|
||||||
|
### UDG2
|
||||||
|
|
||||||
|
See [UDG1](#udg1)
|
||||||
|
|
||||||
|
### G
|
||||||
|
|
||||||
|
Sends a custom group to the next group available: `G=F100FFFFFFFF` or `G=00002000AAAAAAAA` for RDS2
|
||||||
|
|
||||||
|
### RT1EN
|
||||||
|
|
||||||
|
Enables RT 1: `RT1EN=1`
|
||||||
|
|
||||||
|
TODO: Rest of commands
|
||||||
@@ -118,8 +118,8 @@ static void handle_ct(char *arg, RDSModulator* mod, char* output) {
|
|||||||
strcpy(output, "+\0");
|
strcpy(output, "+\0");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_di(char *arg, RDSModulator* mod, char* output) {
|
static void handle_dpty(char *arg, RDSModulator* mod, char* output) {
|
||||||
mod->enc->data[mod->enc->program].di = atoi(arg);
|
mod->enc->data[mod->enc->program].dpty = atoi(arg);
|
||||||
strcpy(output, "+\0");
|
strcpy(output, "+\0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,7 +504,6 @@ static const command_handler_t commands_eq3[] = {
|
|||||||
{"PI", handle_pi, 2},
|
{"PI", handle_pi, 2},
|
||||||
{"TP", handle_tp, 2},
|
{"TP", handle_tp, 2},
|
||||||
{"TA", handle_ta, 2},
|
{"TA", handle_ta, 2},
|
||||||
{"DI", handle_di, 2},
|
|
||||||
{"CT", handle_ct, 2},
|
{"CT", handle_ct, 2},
|
||||||
{"AF", handle_af, 2}
|
{"AF", handle_af, 2}
|
||||||
};
|
};
|
||||||
@@ -525,6 +524,7 @@ static const command_handler_t commands_eq5[] = {
|
|||||||
{"AFCH", handle_afch, 4},
|
{"AFCH", handle_afch, 4},
|
||||||
{"UDG1", handle_udg1, 4},
|
{"UDG1", handle_udg1, 4},
|
||||||
{"UDG2", handle_udg2, 4},
|
{"UDG2", handle_udg2, 4},
|
||||||
|
{"DPTY", handle_dpty, 4},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const command_handler_t commands_eq2[] = {
|
static const command_handler_t commands_eq2[] = {
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc) {
|
|||||||
|
|
||||||
rdsMod->enc = enc;
|
rdsMod->enc = enc;
|
||||||
|
|
||||||
|
if(STREAMS > 0) rdsMod->data[1].symbol_shifting.symbol_shift = SAMPLES_PER_BIT / 2;
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
for (uint8_t i = 0; i < 2; i++) {
|
||||||
for (uint16_t j = 0; j < FILTER_SIZE; j++) {
|
for (uint16_t j = 0; j < FILTER_SIZE; j++) {
|
||||||
waveform[i][j] = i ?
|
waveform[i][j] = i ?
|
||||||
@@ -109,7 +111,15 @@ float get_rds_sample(RDSModulator* rdsMod, uint8_t stream) {
|
|||||||
}
|
}
|
||||||
rdsMod->data[stream].sample_count++;
|
rdsMod->data[stream].sample_count++;
|
||||||
|
|
||||||
sample = rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index];
|
if(rdsMod->data[stream].symbol_shifting.symbol_shift != 0) {
|
||||||
|
rdsMod->data[stream].symbol_shifting.sample_buffer[rdsMod->data[stream].symbol_shifting.sample_buffer_idx++] = rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index];
|
||||||
|
|
||||||
|
if (rdsMod->data[stream].symbol_shifting.sample_buffer_idx == rdsMod->data[stream].symbol_shifting.symbol_shift) rdsMod->data[stream].symbol_shifting.sample_buffer_idx = 0;
|
||||||
|
|
||||||
|
sample = rdsMod->data[stream].symbol_shifting.sample_buffer[rdsMod->data[stream].symbol_shifting.sample_buffer_idx];
|
||||||
|
} else {
|
||||||
|
sample = rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index];
|
||||||
|
}
|
||||||
|
|
||||||
rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index++] = 0;
|
rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index++] = 0;
|
||||||
if (rdsMod->data[stream].out_sample_index == SAMPLE_BUFFER_SIZE)
|
if (rdsMod->data[stream].out_sample_index == SAMPLE_BUFFER_SIZE)
|
||||||
|
|||||||
@@ -11,6 +11,13 @@ typedef struct
|
|||||||
uint8_t rdsgen : 2;
|
uint8_t rdsgen : 2;
|
||||||
} RDSModulatorParameters;
|
} RDSModulatorParameters;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t symbol_shift: 7;
|
||||||
|
float sample_buffer[SAMPLE_BUFFER_SIZE];
|
||||||
|
uint8_t sample_buffer_idx : 7;
|
||||||
|
} RDSModulatorSymbolShifting;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint8_t bit_buffer[BITS_PER_GROUP];
|
uint8_t bit_buffer[BITS_PER_GROUP];
|
||||||
@@ -22,6 +29,7 @@ typedef struct
|
|||||||
uint8_t sample_count;
|
uint8_t sample_count;
|
||||||
uint16_t in_sample_index;
|
uint16_t in_sample_index;
|
||||||
uint16_t out_sample_index;
|
uint16_t out_sample_index;
|
||||||
|
RDSModulatorSymbolShifting symbol_shifting;
|
||||||
} RDSModulatorModulationData;
|
} RDSModulatorModulationData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ void saveToFile(RDSEncoder *emp, const char *option) {
|
|||||||
tempEncoder.data[emp->program].tp = emp->data[emp->program].tp;
|
tempEncoder.data[emp->program].tp = emp->data[emp->program].tp;
|
||||||
} else if (strcmp(option, "TA") == 0) {
|
} else if (strcmp(option, "TA") == 0) {
|
||||||
tempEncoder.data[emp->program].ta = emp->data[emp->program].ta;
|
tempEncoder.data[emp->program].ta = emp->data[emp->program].ta;
|
||||||
} else if (strcmp(option, "DI") == 0) {
|
} else if (strcmp(option, "DPTY") == 0) {
|
||||||
tempEncoder.data[emp->program].di = emp->data[emp->program].di;
|
tempEncoder.data[emp->program].dpty = emp->data[emp->program].dpty;
|
||||||
} else if (strcmp(option, "CT") == 0) {
|
} else if (strcmp(option, "CT") == 0) {
|
||||||
tempEncoder.data[emp->program].ct = emp->data[emp->program].ct;
|
tempEncoder.data[emp->program].ct = emp->data[emp->program].ct;
|
||||||
} else if (strcmp(option, "RT1") == 0 || strcmp(option, "TEXT") == 0) {
|
} else if (strcmp(option, "RT1") == 0 || strcmp(option, "TEXT") == 0) {
|
||||||
@@ -201,8 +201,7 @@ static void get_rds_ps_group(RDSEncoder* enc, uint16_t *blocks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
blocks[1] |= enc->data[enc->program].ta << 4;
|
blocks[1] |= enc->data[enc->program].ta << 4;
|
||||||
blocks[1] |= 1 << 3; // MS was removed from the standard, this is to keep compatibility with old receivers
|
if(enc->state[enc->program].ps_csegment == 0) blocks[1] |= enc->data[enc->program].dpty << 2;
|
||||||
blocks[1] |= ((enc->data[enc->program].di >> (3 - enc->state[enc->program].ps_csegment)) & 1) << 2;
|
|
||||||
blocks[1] |= enc->state[enc->program].ps_csegment;
|
blocks[1] |= enc->state[enc->program].ps_csegment;
|
||||||
blocks[2] = get_next_af(enc);
|
blocks[2] = get_next_af(enc);
|
||||||
if(enc->data[enc->program].ta && enc->state[enc->program].tps_text[0] != '\0') {
|
if(enc->data[enc->program].ta && enc->state[enc->program].tps_text[0] != '\0') {
|
||||||
@@ -712,7 +711,6 @@ void set_rds_defaults(RDSEncoder* enc, uint8_t program) {
|
|||||||
enc->encoder_data.encoder_addr[1] = 255;
|
enc->encoder_data.encoder_addr[1] = 255;
|
||||||
|
|
||||||
enc->data[program].ct = 1;
|
enc->data[program].ct = 1;
|
||||||
enc->data[program].di = 1;
|
|
||||||
strcpy((char *)enc->data[program].grp_sqc, DEFAULT_GRPSQC);
|
strcpy((char *)enc->data[program].grp_sqc, DEFAULT_GRPSQC);
|
||||||
enc->data[program].tp = 1;
|
enc->data[program].tp = 1;
|
||||||
enc->data[program].pi = 0xFFFF;
|
enc->data[program].pi = 0xFFFF;
|
||||||
|
|||||||
Reference in New Issue
Block a user