0
1
mirror of https://github.com/radio95-rnt/rds95.git synced 2026-02-26 12:32:05 +01:00

rtp in lua

This commit is contained in:
2025-12-26 11:15:47 +01:00
parent 9c4321d9e4
commit d48a006b90
10 changed files with 103 additions and 180 deletions

View File

@@ -220,18 +220,20 @@ function put_rds_custom_group(b, c, d) end
---@param d integer
function put_rds2_custom_group(a, b, c, d) end
---This is implemented externally
---Toggles RTP or ERTP's toggle switch
---@param ertp boolean
function toggle_rds_rtp(ertp) end
---This is implemented externally
---Sets the metadata of RTP or ERTP
---@param ertp boolean
---@param enabled boolean
---@param running boolean
function set_rds_rtp_meta(ertp, enabled, running) end
function set_rds_rtp_meta(ertp, running) end
---This is implemented externally
---Gets the metadata of RTP and ERTP
---@param ertp boolean
---@return boolean enabled, boolean running
---@return boolean running
function get_rds_rtp_meta(ertp) end
---Sets the AFs included in group 0

View File

@@ -27,6 +27,14 @@ The newer standard which is the IEC one, removes these features:
RDS95 is the only (as far as i can tell) encoder to transmit the 9-bit AF codes
## Lua engine
Yet another unique feature, the Lua engine allows you to parse the messages from UDP, and more!
You wanna make an ODA? Sure go for it, look in the scripts folder to see `0-ert.lua`, that is how ERT is implemmented in this encoder
Wanna do scrolling PS without C stuff and no external scripts? Just do it with the `tick` function!
Don't wanna run a bash script to send your station's data to the encoder when defaults take over? I'll welcome you to the `on_init` function
## Disclaimer
RDS95 is based on [Anthony96922](https://github.com/Anthony96922/)'s [MiniRDS](https://github.com/Anthony96922/MiniRDS)
RDS95 is "based" on [Anthony96922](https://github.com/Anthony96922/)'s [MiniRDS](https://github.com/Anthony96922/MiniRDS) licensed under GPLv3

80
scripts/0-rtp.lua Normal file
View File

@@ -0,0 +1,80 @@
_Rtp_oda_id = nil
_Ertp_oda_id = nil
_Rtp_toggle = true
_Ertp_toggle = true
local function init_rtp()
if _Rtp_oda_id == nil then
_Rtp_oda_id = register_oda(11, false, 0x4BD7, 0)
set_oda_handler(_Rtp_oda_id, function ()
local b = (_Rtp_toggle and 1 or 0) << 4 | string.byte(get_userdata_offset(259, 1)) << 3
local data_0 = get_userdata_offset(260, 3)
local data_1 = get_userdata_offset(263, 3)
b = b | (string.byte(data_0, 1) & 0xf8) >> 3
local c = (string.byte(data_0, 1) & 0x7) << 13
c = c | (string.byte(data_0, 2) & 0x3f) << 7
c = c | (string.byte(data_0, 3) & 0x3f) << 1
c = c | (string.byte(data_1, 1) & 0xe0) >> 5
local d = (string.byte(data_1, 1) & 0x1f) << 11
d = d | (string.byte(data_1, 2) & 0x3f) << 5
d = d | (string.byte(data_1, 3) & 0x1f)
return b, c, d
end)
end
end
local function init_ertp()
if _Ertp_oda_id == nil then
_Ertp_oda_id = register_oda(12, false, 0x4BD8, 0)
set_oda_handler(_Ertp_oda_id, function ()
local b = (_Ertp_toggle and 1 or 0) << 4 | string.byte(get_userdata_offset(266, 1)) << 3
local data_0 = get_userdata_offset(267, 3)
local data_1 = get_userdata_offset(270, 3)
b = b | (string.byte(data_0, 1) & 0xf8) >> 3
local c = (string.byte(data_0, 1) & 0x7) << 13
c = c | (string.byte(data_0, 2) & 0x3f) << 7
c = c | (string.byte(data_0, 3) & 0x3f) << 1
c = c | (string.byte(data_1, 1) & 0xe0) >> 5
local d = (string.byte(data_1, 1) & 0x1f) << 11
d = d | (string.byte(data_1, 2) & 0x3f) << 5
d = d | (string.byte(data_1, 3) & 0x1f)
return b, c, d
end)
end
end
function set_rds_rtp_meta(ertp, running)
if ertp then
if running and _Ertp_oda_id == nil then init_ertp() end
set_userdata_offset(266, 1, string.char(running and 1 or 0))
else
if running and _Rtp_oda_id == nil then init_rtp() end
set_userdata_offset(259, 1, string.char(running and 1 or 0))
end
end
function get_rds_rtp_meta(ertp)
local offset = ertp and 266 or 259
return string.byte(get_userdata_offset(offset, 1)) ~= 0
end
function toggle_rds_rtp(ertp)
if ertp then _Ertp_toggle = not _Ertp_toggle
else _Rtp_toggle = not _Rtp_toggle end
end
function set_rds_rtplus_tags(ertp, t1, s1, l1, t2, s2, l2)
set_userdata_offset(ertp and 267 or 260, 6, string.char(t1, s1, l1, t2, s2, l2))
end
function get_rds_rtplus_tags(ertp)
return string.byte(get_userdata_offset(ertp and 267 or 260, 6), 1, 6)
end
function on_state()
if get_rds_rtp_meta(false) then init_rtp() end
if get_rds_rtp_meta(true) then init_ertp() end
end

View File

@@ -36,12 +36,12 @@ function data_handle(data)
local t1, s1, l1, t2, s2, l2 = get_rds_rtplus_tags(true)
return string.format("ERTP=%d,%d,%d,%d,%d,%d\r\n", t1, s1, l1, t2, s2, l2)
elseif data == "rtprun" then
local enabled, running = get_rds_rtp_meta(false)
local f1 = enabled and 2 or (running and 1 or 0)
local running = get_rds_rtp_meta(false)
local f1 = 2 or (running and 1 or 0)
return string.format("RTPRUN=%d\r\n", f1)
elseif data == "ertprun" then
local enabled, running = get_rds_rtp_meta(true)
local f1 = enabled and 2 or (running and 1 or 0)
local running = get_rds_rtp_meta(true)
local f1 = 2 or (running and 1 or 0)
return string.format("ERTPRUN=%d\r\n", f1)
elseif data == "lps" then return string.format("LPS=%s\r\n", get_rds_lps())
elseif data == "ert" then return string.format("ERT=%s\r\n", get_rds_ert())
@@ -322,10 +322,9 @@ function data_handle(data)
local f1 = tonumber(f1_str) or 0
local f2 = tonumber(f2_str) or 0
local enabled = (f1 == 2)
local running = (f1 & 1) ~= 0
set_rds_rtp_meta(is_ertp, enabled, running)
set_rds_rtp_meta(is_ertp, running)
if f2 ~= 0 then toggle_rds_rtp(is_ertp) end
return "+"
elseif cmd == "af" then

View File

@@ -14,13 +14,11 @@ void encoder_saveToFile(RDSEncoder *enc) {
} else memcpy(&tempEncoder, enc, sizeof(RDSEncoder));
memcpy(tempEncoder.data, enc->data, sizeof(RDSData) * PROGRAMS);
memcpy(tempEncoder.rtpData, enc->rtpData, sizeof(RDSRTPlusData) * PROGRAMS * 2);
memcpy(&tempEncoder.encoder_data, &enc->encoder_data, sizeof(RDSEncoderData));
tempEncoder.program = enc->program;
RDSEncoderFile rdsEncoderfile = {.file_starter = 225, .file_middle = 160, .file_ender = 95, .program = tempEncoder.program};
memcpy(&rdsEncoderfile.data, &tempEncoder.data, sizeof(RDSData)*PROGRAMS);
memcpy(&rdsEncoderfile.rtpData, &tempEncoder.rtpData, sizeof(RDSRTPlusData) * 2 * PROGRAMS);
memcpy(&rdsEncoderfile.encoder_data, &tempEncoder.encoder_data, sizeof(RDSEncoderData));
rdsEncoderfile.crc = crc16_ccitt((char *)&rdsEncoderfile, offsetof(RDSEncoderFile, crc));
@@ -59,7 +57,6 @@ int encoder_loadFromFile(RDSEncoder *enc) {
}
memcpy(&(enc->data), &(rdsEncoderfile.data), sizeof(RDSData)*PROGRAMS);
memcpy(&(enc->rtpData), &(rdsEncoderfile.rtpData), sizeof(RDSRTPlusData)*2*PROGRAMS);
memcpy(&(enc->encoder_data), &(rdsEncoderfile.encoder_data), sizeof(RDSEncoderData));
enc->program = rdsEncoderfile.program;
return 0;

View File

@@ -243,32 +243,6 @@ int lua_get_rds_level(lua_State *localL) {
return 1;
}
int lua_set_rds_rtplus_tags(lua_State *localL) {
uint8_t tags[6];
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
int ertp = lua_toboolean(localL, 1);
tags[0] = luaL_checkinteger(localL, 2);
tags[1] = luaL_checkinteger(localL, 3);
tags[2] = luaL_checkinteger(localL, 4);
tags[3] = luaL_checkinteger(localL, 5);
tags[4] = luaL_checkinteger(localL, 6);
tags[5] = luaL_checkinteger(localL, 7);
if(ertp == 1) set_rds_ertplus_tags(mod->enc, tags);
else set_rds_rtplus_tags(mod->enc, tags);
return 0;
}
int lua_get_rds_rtplus_tags(lua_State *localL) {
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
int ertp = lua_toboolean(localL, 1);
lua_pushinteger(localL, mod->enc->rtpData[mod->enc->program][ertp].type[0]);
lua_pushinteger(localL, mod->enc->rtpData[mod->enc->program][ertp].start[0]);
lua_pushinteger(localL, mod->enc->rtpData[mod->enc->program][ertp].len[0]);
lua_pushinteger(localL, mod->enc->rtpData[mod->enc->program][ertp].type[1]);
lua_pushinteger(localL, mod->enc->rtpData[mod->enc->program][ertp].start[1]);
lua_pushinteger(localL, mod->enc->rtpData[mod->enc->program][ertp].len[1]);
return 6;
}
int lua_put_rds_custom_group(lua_State *localL) {
mod->enc->state[mod->enc->program].custom_group[0] = 1;
mod->enc->state[mod->enc->program].custom_group[1] = luaL_checkinteger(localL, 1);
@@ -285,30 +259,6 @@ int lua_put_rds2_custom_group(lua_State *localL) {
return 0;
}
int lua_toggle_rds_rtp(lua_State *localL) {
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
int ertp = lua_toboolean(localL, 1);
TOGGLE(mod->enc->rtpState[mod->enc->program][ertp].toggle);
return 0;
}
int lua_set_rds_rtp_meta(lua_State *localL) {
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
if (!lua_isboolean(localL, 2)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 2));
if (!lua_isboolean(localL, 3)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 3));
int ertp = lua_toboolean(localL, 1);
mod->enc->rtpData[mod->enc->program][ertp].enabled = lua_toboolean(localL, 2);
mod->enc->rtpData[mod->enc->program][ertp].running = lua_toboolean(localL, 3);
return 0;
}
int lua_get_rds_rtp_meta(lua_State *localL) {
if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1));
int ertp = lua_toboolean(localL, 1);
lua_pushboolean(localL, mod->enc->rtpData[mod->enc->program][ertp].enabled);
lua_pushboolean(localL, mod->enc->rtpData[mod->enc->program][ertp].running);
return 2;
}
STR_SETTER(ptyn, set_rds_ptyn)
STR_SETTER(ps, set_rds_ps)
STR_SETTER(tps, set_rds_tps)
@@ -595,13 +545,6 @@ void init_lua(RDSModulator* rds_mod) {
lua_register(L, "set_rds_lps", lua_set_rds_lps);
lua_register(L, "get_rds_lps", lua_get_rds_lps);
lua_register(L, "set_rds_rtplus_tags", lua_set_rds_rtplus_tags);
lua_register(L, "get_rds_rtplus_tags", lua_get_rds_rtplus_tags);
lua_register(L, "toggle_rds_rtp", lua_toggle_rds_rtp);
lua_register(L, "set_rds_rtp_meta", lua_set_rds_rtp_meta);
lua_register(L, "get_rds_rtp_meta", lua_get_rds_rtp_meta);
lua_register(L, "put_rds_custom_group", lua_put_rds_custom_group);
lua_register(L, "put_rds2_custom_group", lua_put_rds2_custom_group);

View File

@@ -12,8 +12,6 @@ uint16_t get_next_af_eon(RDSEncoder* enc, uint8_t eon_index);
void get_rds_ps_group(RDSEncoder* enc, RDSGroup *group);
void get_rds_fasttuning_group(RDSEncoder* enc, RDSGroup *group);
void get_rds_rt_group(RDSEncoder* enc, RDSGroup *group);
void get_rdsp_rtp_oda_group(RDSGroup *group);
void get_rdsp_ertp_oda_group(RDSGroup *group);
void get_rdsp_oda_af_oda_group(RDSGroup *group);
void get_rds_oda_af_group(RDSEncoder* enc, RDSGroup *group);
void get_rdsp_ct_group(RDSGroup *group, time_t now);
@@ -21,8 +19,6 @@ void get_rds_lps_group(RDSEncoder* enc, RDSGroup *group);
void get_rds_ecc_group(RDSEncoder* enc, RDSGroup *group);
void get_rds_slcdata_group(RDSEncoder* enc, RDSGroup *group);
void get_rds_ptyn_group(RDSEncoder* enc, RDSGroup *group);
void get_rds_rtplus_group(RDSEncoder* enc, RDSGroup *group);
void get_rds_ertplus_group(RDSEncoder* enc, RDSGroup *group);
void get_rds_eon_group(RDSEncoder* enc, RDSGroup *group);
uint8_t get_rds_custom_groups(RDSEncoder* enc, RDSGroup *group);
uint8_t get_rds_custom_groups2(RDSEncoder* enc, RDSGroup *group);
@@ -88,16 +84,6 @@ static void get_rds_sequence_group(RDSEncoder* enc, RDSGroup *group, char* grp,
case 'Y':
HANDLE_UDG_STREAM(1, udg2);
break;
case 'R':
if(enc->state[enc->program].rtp_oda == 0) get_rds_rtplus_group(enc, group);
else get_rdsp_rtp_oda_group(group);
TOGGLE(enc->state[enc->program].rtp_oda);
break;
case 'P':
if(enc->state[enc->program].ert_oda == 0) get_rds_ertplus_group(enc, group);
else get_rdsp_ertp_oda_group(group);
TOGGLE(enc->state[enc->program].ert_oda);
break;
case 'F':
get_rds_lps_group(enc, group);
break;
@@ -110,6 +96,8 @@ static void get_rds_sequence_group(RDSEncoder* enc, RDSGroup *group, char* grp,
case 'O':
get_rds_user_oda_group(enc, group);
break;
case 'R':
case 'P':
case 'S':
case 'K':
if(get_rds_user_oda_group_content(enc, group) == 0) get_rds_ps_group(enc, group);
@@ -138,8 +126,6 @@ static uint8_t check_rds_good_group(RDSEncoder* enc, char* grp) {
}
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][0].enabled) good_group = 1;
if(*grp == 'P' && enc->rtpData[enc->program][1].enabled) good_group = 1;
if(*grp == 'F' && enc->data[enc->program].lps[0] != '\0') good_group = 1;
if(*grp == 'T') good_group = 1;
if(*grp == 'L') good_group = 1;
@@ -352,7 +338,6 @@ void reset_rds_state(RDSEncoder* enc, uint8_t program) {
RDSEncoder tempCoder;
tempCoder.program = program;
memset(&(tempCoder.state[program]), 0, sizeof(RDSState));
memset(&(tempCoder.rtpState[program]), 0, sizeof(RDSRTPlusState)*2);
tempCoder.state[program].rt_ab = 1;
tempCoder.state[program].ptyn_ab = 1;
@@ -375,12 +360,10 @@ void reset_rds_state(RDSEncoder* enc, uint8_t program) {
for(int i = 0; i < EONs; i++) tempCoder.data[program].eon[i].ta = 0;
memcpy(&(enc->state[program]), &(tempCoder.state[program]), sizeof(RDSState));
memcpy(&(enc->rtpState[program]), &(tempCoder.rtpState[program]), sizeof(RDSRTPlusState));
}
void set_rds_defaults(RDSEncoder* enc, uint8_t program) {
memset(&(enc->data[program]), 0, sizeof(RDSData));
memset(&(enc->rtpData[program]), 0, sizeof(RDSRTPlusData)*2);
memset(&(enc->encoder_data), 0, sizeof(RDSEncoderData));
enc->data[program].ct = 1;

View File

@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#define LUA_USER_DATA 768
#define LUA_USER_DATA 1024
/* The RDS error-detection code generator polynomial is
* x^10 + x^8 + x^7 + x^5 + x^4 + x^3 + x^0
@@ -17,7 +17,7 @@
#define PS_LENGTH 8
#define PTYN_LENGTH 8
#define LPS_LENGTH 32
#define DEFAULT_GRPSQC "002222XY"
#define DEFAULT_GRPSQC "002222XYOK"
#define MAX_AFS 25
#define AF_CODE_FILLER 205
@@ -28,8 +28,6 @@
#define EONs 4
// List of ODAs: https://www.nrscstandards.org/committees/dsm/archive/rds-oda-aids.pdf
#define ODA_AID_RTPLUS 0x4bd7
#define ODA_AID_ERTPLUS 0x4BD8
#define ODA_AID_ODAAF 0x6365
typedef struct {
@@ -155,9 +153,6 @@ typedef struct {
uint8_t rt_switching_period_state;
uint8_t rt_text_timeout_state;
uint8_t rtp_oda : 1;
uint8_t ertp_oda : 1;
uint8_t ert_oda : 1;
uint8_t af_oda : 1;
uint8_t data_ecc : 1;
uint8_t grp_seq_idx[4];
@@ -187,17 +182,6 @@ typedef struct {
RDSODAState user_oda;
} RDSState;
typedef struct {
uint8_t enabled : 1;
uint8_t running : 1;
uint8_t type[2];
uint8_t start[2];
uint8_t len[2];
} RDSRTPlusData;
typedef struct {
uint8_t toggle : 1;
} RDSRTPlusState;
typedef struct {
uint16_t special_features;
uint8_t rds2_mode : 1;
@@ -207,14 +191,11 @@ typedef struct {
RDSEncoderData encoder_data;
RDSData data[PROGRAMS];
RDSState state[PROGRAMS];
RDSRTPlusData rtpData[PROGRAMS][2];
RDSRTPlusState rtpState[PROGRAMS][2];
uint8_t program : 3;
} RDSEncoder;
typedef struct {
uint8_t file_starter; // Always is 225 first polish radio programme am frequency
RDSData data[PROGRAMS];
RDSRTPlusData rtpData[PROGRAMS][2];
uint8_t file_middle; // Always is 160, average of both
RDSEncoderData encoder_data;
uint8_t program : 3;
@@ -245,8 +226,6 @@ void set_rds_default_rt(RDSEncoder* enc, const char *rt);
void set_rds_ps(RDSEncoder* enc, const char *ps);
void set_rds_tps(RDSEncoder* enc, const char *tps);
void set_rds_lps(RDSEncoder* enc, const char *lps);
void set_rds_rtplus_tags(RDSEncoder *enc, uint8_t *tags);
void set_rds_ertplus_tags(RDSEncoder *enc, uint8_t *tags);
void set_rds_ptyn(RDSEncoder *enc, const char *ptyn);
void set_rds_grpseq(RDSEncoder* enc, const char *grpseq);
void set_rds_grpseq2(RDSEncoder* enc, const char *grpseq2);

View File

@@ -136,18 +136,6 @@ void get_rds_rt_group(RDSEncoder* enc, RDSGroup *group) {
if (enc->state[enc->program].rt_state == segments) enc->state[enc->program].rt_state = 0;
}
void get_rdsp_rtp_oda_group(RDSGroup *group) {
group->b |= 3 << 12;
group->b |= 11 << 1;
group->d = ODA_AID_RTPLUS;
}
void get_rdsp_ertp_oda_group(RDSGroup *group) {
group->b |= 3 << 12;
group->b |= 13 << 1;
group->d = ODA_AID_ERTPLUS;
}
void get_rdsp_oda_af_oda_group(RDSGroup *group) {
group->b |= 3 << 12;
group->b |= 7 << 1;
@@ -236,36 +224,6 @@ void get_rds_ptyn_group(RDSEncoder* enc, RDSGroup *group) {
if (enc->state[enc->program].ptyn_state == 2) enc->state[enc->program].ptyn_state = 0;
}
void get_rds_rtplus_group(RDSEncoder* enc, RDSGroup *group) {
group->b |= 11 << 12;
group->b |= enc->rtpState[enc->program][0].toggle << 4 | enc->rtpData[enc->program][0].running << 3;
group->b |= (enc->rtpData[enc->program][0].type[0] & 0xf8) >> 3;
group->c = (enc->rtpData[enc->program][0].type[0] & 0x07) << 13;
group->c |= (enc->rtpData[enc->program][0].start[0] & 0x3f) << 7;
group->c |= (enc->rtpData[enc->program][0].len[0] & 0x3f) << 1;
group->c |= (enc->rtpData[enc->program][0].type[1] & 0xe0) >> 5;
group->d = (enc->rtpData[enc->program][0].type[1] & 0x1f) << 11;
group->d |= (enc->rtpData[enc->program][0].start[1] & 0x3f) << 5;
group->d |= enc->rtpData[enc->program][0].len[1] & 0x1f;
}
void get_rds_ertplus_group(RDSEncoder* enc, RDSGroup *group) {
group->b |= 13 << 12;
group->b |= enc->rtpState[enc->program][1].toggle << 4 | enc->rtpData[enc->program][1].running << 3;
group->b |= (enc->rtpData[enc->program][1].type[0] & 0xf8) >> 3;
group->c = (enc->rtpData[enc->program][1].type[0] & 0x07) << 13;
group->c |= (enc->rtpData[enc->program][1].start[0] & 0x3f) << 7;
group->c |= (enc->rtpData[enc->program][1].len[0] & 0x3f) << 1;
group->c |= (enc->rtpData[enc->program][1].type[1] & 0xe0) >> 5;
group->d = (enc->rtpData[enc->program][1].type[1] & 0x1f) << 11;
group->d |= (enc->rtpData[enc->program][1].start[1] & 0x3f) << 5;
group->d |= enc->rtpData[enc->program][1].len[1] & 0x1f;
}
void get_rds_eon_group(RDSEncoder* enc, RDSGroup *group) {
RDSEON eon;
eon = enc->data[enc->program].eon[enc->state[enc->program].eon_index];

View File

@@ -106,32 +106,6 @@ void set_rds_lps(RDSEncoder* enc, const char *lps) {
if(enc->state[enc->program].lps_segments > 8) enc->state[enc->program].lps_segments = 8; //make sure
}
inline void set_rds_rtplus_tags(RDSEncoder* enc, uint8_t *tags) {
enc->rtpData[enc->program][0].type[0] = tags[0] & 0x3f;
enc->rtpData[enc->program][0].start[0] = tags[1] & 0x3f;
enc->rtpData[enc->program][0].len[0] = tags[2] & 0x3f;
enc->rtpData[enc->program][0].type[1] = tags[3] & 0x3f;
enc->rtpData[enc->program][0].start[1] = tags[4] & 0x3f;
enc->rtpData[enc->program][0].len[1] = tags[5] & 0x1f;
TOGGLE(enc->rtpState[enc->program][0].toggle);
enc->rtpData[enc->program][0].running = 1;
enc->rtpData[enc->program][0].enabled = 1;
}
inline void set_rds_ertplus_tags(RDSEncoder* enc, uint8_t *tags) {
enc->rtpData[enc->program][1].type[0] = tags[0] & 0x3f;
enc->rtpData[enc->program][1].start[0] = tags[1] & 0x3f;
enc->rtpData[enc->program][1].len[0] = tags[2] & 0x3f;
enc->rtpData[enc->program][1].type[1] = tags[3] & 0x3f;
enc->rtpData[enc->program][1].start[1] = tags[4] & 0x3f;
enc->rtpData[enc->program][1].len[1] = tags[5] & 0x1f;
TOGGLE(enc->rtpState[enc->program][1].toggle);
enc->rtpData[enc->program][1].running = 1;
enc->rtpData[enc->program][1].enabled = 1;
}
void set_rds_ptyn(RDSEncoder* enc, const char *ptyn) {
uint8_t len = 0;