From d579fa1dfc9fac3fd6b88b6eaa92bb387f016d93 Mon Sep 17 00:00:00 2001 From: KubaPro010 Date: Wed, 22 Apr 2026 19:46:24 +0200 Subject: [PATCH] proper uecp --- .vscode/settings.json | 3 +- CMakeLists.txt | 2 +- plugin.lua | 63 +++------ scripts/0-oda.lua | 2 +- scripts/0-rds2_oda.lua | 2 +- scripts/1-af.lua | 2 +- scripts/1-ert.lua | 2 +- scripts/1-rtp.lua | 2 +- scripts/98-uecp.lua | 281 +++++++++++++++++++++++++++++++++++++++++ scripts/99-data.lua | 154 ++-------------------- src/lua_api.c | 33 ++--- src/lua_api.h | 22 +--- src/lua_rds.c | 48 ++++--- src/rds.c | 36 +----- src/rds.h | 21 +-- src/rds_groups.c | 35 ++--- src/rds_setters.c | 54 +------- 17 files changed, 381 insertions(+), 381 deletions(-) create mode 100644 scripts/98-uecp.lua diff --git a/.vscode/settings.json b/.vscode/settings.json index bff6bd0..e68941f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,6 +32,7 @@ }, "C_Cpp.dimInactiveRegions": false, "Lua.diagnostics.disable": [ - "duplicate-set-field" + "duplicate-set-field", + "lowercase-global" ] } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d42a20..451d165 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.10) -project(rds95 VERSION 1.7) +project(rds95 VERSION 1.8) add_compile_options(-Wall -Werror -Wextra -pedantic -O2 -std=c18 -march=native -DVERSION=\"${PROJECT_VERSION}\") diff --git a/plugin.lua b/plugin.lua index 9719555..122931f 100644 --- a/plugin.lua +++ b/plugin.lua @@ -40,29 +40,32 @@ function dp.set_writing_program(program_idx) end ---@return integer function dp.get_writing_program() end +hooks = {} + ---This function is called by the C core after we reset data, or have no data in general ---It should be defined by the user in the script. ----@return nil -function on_init() end -on_inits = {} +---This is a table of functions. Each will be called +hooks.on_init = {} ---This function is called by the C core after we initialize the encoder (always, every start) ---It should be defined by the user in the script. ----@return nil -function on_start() end -on_starts = {} +---This is a table of functions. Each will be called +hooks.on_start = {} ---This function is called every time when the state resets, register your odas here ---It should be defined by the user in the script. ----@return nil -function on_state() end -on_states = {} +---This is a table of functions. Each will be called +hooks.on_state = {} ---This function is called every second ---It should be defined by the user in the script. ----@return nil -function tick() end -ticks = {} +---This is a table of functions. Each will be called +hooks.tick = {} + +---This function is calld every time a RT transmits start to end - Useful for UECP's RT buffer shenanigans +---It should be defined by the user in the script. +---This is a table of functions. Each will be called +hooks.rt_transmission = {} -- Every function with with the s suffixed table means that other versions of that function can be in that list-table, and each one will be called @@ -140,25 +143,15 @@ function rds.get_ta() end -- Feature Flags ---@param enabled boolean -function rds.set_rt1_enabled(enabled) end +function rds.set_rt_enabled(enabled) end ---@return boolean -function rds.get_rt1_enabled() end - ----@param enabled boolean -function rds.set_rt2_enabled(enabled) end ----@return boolean -function rds.get_rt2_enabled() end +function rds.get_rt_enabled() end ---@param enabled boolean function rds.set_ptyn_enabled(enabled) end ---@return boolean function rds.get_ptyn_enabled() end ----@param rt_type integer -function rds.set_rt_type(rt_type) end ----@return integer -function rds.get_rt_type() end - -- Modulation & Generation ---@param mode integer function rds.set_rds2_mode(mode) end @@ -176,18 +169,6 @@ function rds.set_link(linkage) end ---@return boolean function rds.get_link() end --- Timeouts and Periods ----@param period integer in seconds -function rds.set_rt_switching_period(period) end ----@return integer -function rds.get_rt_switching_period() end - ----For a RT1, this sets the timeout period before setting RT1 into "Default RT" (not literally) ----@param timeout integer in seconds -function rds.set_rt_text_timeout(timeout) end ----@return integer -function rds.get_rt_text_timeout() end - -- String Setters (Charset converted) ---@param ptyn string Program Type Name (max 8 chars) function rds.set_ptyn(ptyn) end @@ -195,12 +176,10 @@ function rds.set_ptyn(ptyn) end function rds.set_ps(ps) end ---@param tps string Traffic PS function rds.set_tps(tps) end ----@param rt1 string Radio Text 1 (max 64 chars) -function rds.set_rt1(rt1) end ----@param rt2 string Radio Text 2 (max 64 chars) -function rds.set_rt2(rt2) end ----@param rt string Default radio text - max 64 characters -function rds.set_default_rt(rt) end +---@param rt string Radio Text (max 64 chars) +function rds.set_rt(rt) end + +function rds.toggle_rt_ab() end ---@param lps string function rds.set_lps(lps) end diff --git a/scripts/0-oda.lua b/scripts/0-oda.lua index 3964fcb..d799b00 100644 --- a/scripts/0-oda.lua +++ b/scripts/0-oda.lua @@ -119,7 +119,7 @@ function group(group_type) return false, 0, 0, 0 end -table.insert(on_states, function () +table.insert(hooks.on_state, function () _RDS_ODAs = {} _RDS_ODA_pointer = 1 end) \ No newline at end of file diff --git a/scripts/0-rds2_oda.lua b/scripts/0-rds2_oda.lua index 0173b31..3cee014 100644 --- a/scripts/0-rds2_oda.lua +++ b/scripts/0-rds2_oda.lua @@ -125,7 +125,7 @@ function rds2_group(stream) end end -table.insert(on_states, function () +table.insert(hooks.on_state, function () _RDS2_ODAs = {} _RDS2_ODA_aid = 0 _RDS2_ODA_pointer = 1 diff --git a/scripts/1-af.lua b/scripts/1-af.lua index 3cf4a3f..268e801 100644 --- a/scripts/1-af.lua +++ b/scripts/1-af.lua @@ -111,7 +111,7 @@ function rds.ext.set_af_oda(afs) save_af_to_userdata(afs) end -table.insert(on_states, function () +table.insert(hooks.on_state, function () load_af_from_userdata() if _Af_Oda_len ~= 0 then init_af_oda() end end) \ No newline at end of file diff --git a/scripts/1-ert.lua b/scripts/1-ert.lua index 2086d29..c8ef6e3 100644 --- a/scripts/1-ert.lua +++ b/scripts/1-ert.lua @@ -80,6 +80,6 @@ function rds.ext.get_ert() return data:match("^(.-)[\r%z]*") or "" end -table.insert(on_states, function () +table.insert(hooks.on_state, function () if string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+257, 1)) ~= 0 then init_ert() end end) \ No newline at end of file diff --git a/scripts/1-rtp.lua b/scripts/1-rtp.lua index 5a95584..54d2c16 100644 --- a/scripts/1-rtp.lua +++ b/scripts/1-rtp.lua @@ -89,7 +89,7 @@ function unregister_rtp(ertp) end end -table.insert(on_states, function () +table.insert(hooks.on_state, function () if rds.ext.get_rtp_meta(false) then init_rtp() end if rds.ext.get_rtp_meta(true) then init_ertp() end end) \ No newline at end of file diff --git a/scripts/98-uecp.lua b/scripts/98-uecp.lua new file mode 100644 index 0000000..8c54f7c --- /dev/null +++ b/scripts/98-uecp.lua @@ -0,0 +1,281 @@ +uecp = {} +uecp.rt_buffer = {} +uecp.rt_buffer_index = 1 +uecp.rt_tx_remaining = 0 + +hooks.rt_transmission[#hooks.rt_transmission + 1] = function () + if #uecp.rt_buffer == 0 then return end + + if #uecp.rt_buffer > 1 and uecp.rt_tx_remaining > 1 then + uecp.rt_tx_remaining = uecp.rt_tx_remaining - 1 + return + end + + uecp.rt_buffer_index = (uecp.rt_buffer_index % #uecp.rt_buffer) + 1 + local entry = uecp.rt_buffer[uecp.rt_buffer_index] + uecp.rt_tx_remaining = entry.number_tx + + rds.set_rt(entry.text) + if entry.toggle_ab then rds.toggle_rt_ab() end +end + +local mec_handlers = {} + +local function dsn_helper(dsn, write) + if dsn == 0 then write() + elseif dsn == 254 then + local start = dp.get_writing_program() + for i = 1, dp.max_programs do + local p = i - 1 + if p ~= start then + dp.set_writing_program(p) + write() + end + end + elseif dsn == 255 then + for i = 1, dp.max_programs do + dp.set_writing_program(i - 1) + write() + end + else + dp.set_writing_program(dsn - 1) + write() + end +end + +mec_handlers[1] = function (data) + -- PI + local dsn = string.byte(data, 2) + local psn = string.byte(data, 3) + local pi_msb = string.byte(data, 4) + local pi_lsb = string.byte(data, 5) + + dsn_helper(dsn, function () + rds.set_pi((pi_msb << 8) | pi_lsb) + end) + return 5 +end +mec_handlers[2] = function (data) + -- PS + local dsn = string.byte(data, 2) + local psn = string.byte(data, 3) + local ps = string.sub(data, 4, 11) -- Static len + dsn_helper(dsn, function () + rds.set_ps(ps) + end) + return 11 +end +mec_handlers[0x21] = function (data) + -- LPS + local dsn = string.byte(data, 2) + local psn = string.byte(data, 3) + local mel = string.byte(data, 4) + local lps = string.sub(data, 5, 5+mel) + + local cut = string.find(lps, "\r", 1, true) + if cut then lps = string.sub(lps, 1, cut - 1) end + + dsn_helper(dsn, function () + rds.set_lps(lps) + end) + return 4 + mel +end +mec_handlers[4] = function (data) + -- PTYI + local dsn = string.byte(data, 2) + local psn = string.byte(data, 3) + local data = string.byte(data, 4) + dsn_helper(dsn, function () + rds.set_dpty((data & 8) ~= 0) + end) + return 4 +end +mec_handlers[3] = function (data) + -- TP/TA + local dsn = string.byte(data, 2) + local psn = string.byte(data, 3) + local data = string.byte(data, 4) + dsn_helper(dsn, function () + rds.set_ta((data & 1) ~= 0) + rds.set_ta((data & 2) ~= 0) + end) + return 4 +end +mec_handlers[7] = function (data) + -- PTY + local dsn = string.byte(data, 2) + local psn = string.byte(data, 3) + local data = string.byte(data, 4) + dsn_helper(dsn, function () + rds.set_pty(data) + end) + return 4 +end +mec_handlers[0x3E] = function (data) + -- PTYN + local dsn = string.byte(data, 2) + local psn = string.byte(data, 3) + local ptyn = string.sub(data, 4, 11) -- Static + dsn_helper(dsn, function () + rds.set_ptyn(ptyn) + end) + return 11 +end +mec_handlers[0x0A] = function (data) + local dsn = string.byte(data, 2) + local psn = string.byte(data, 3) + local mel = string.byte(data, 4) + + if mel == 0 then + uecp.rt_buffer = {} + uecp.rt_buffer_index = 1 + uecp.rt_tx_remaining = 0 + dsn_helper(dsn, function () + rds.set_rt_enabled(false) + end) + return 4 + end + + local config = string.byte(data, 5) + local toggle_ab = (config & 1) ~= 0 + local number_tx = (config >> 1) & 0xF + local buffer_config = (config >> 5) & 3 + + if mel == 1 and buffer_config == 0 then + uecp.rt_buffer = {} + uecp.rt_buffer_index = 1 + uecp.rt_tx_remaining = 0 + dsn_helper(dsn, function () + rds.set_rt_enabled(false) + end) + return 5 + end + + local rt_text = string.sub(data, 6, 4 + mel) + local cr = string.find(rt_text, "\r", 1, true) + if cr then rt_text = string.sub(rt_text, 1, cr - 1) end + + if buffer_config == 0 then + uecp.rt_buffer = {} + uecp.rt_buffer_index = 1 + uecp.rt_buffer[1] = { text = rt_text, number_tx = number_tx, toggle_ab = toggle_ab } + uecp.rt_tx_remaining = number_tx + -- Seed the encoder immediately with the first entry + dsn_helper(dsn, function () + rds.set_rt(rt_text) + if toggle_ab then rds.toggle_rt_ab() end + rds.set_rt_enabled(true) + end) + elseif buffer_config == 2 then + uecp.rt_buffer[#uecp.rt_buffer + 1] = { text = rt_text, number_tx = number_tx, toggle_ab = toggle_ab } + end + + return 4 + mel +end +mec_handlers[0x13] = function (data) + -- AF + local dsn = string.byte(data, 2) + local psn = string.byte(data, 3) + local mel = string.sub(data, 4) + + -- TODO: decode + + return 4+mel +end +mec_handlers[0x1A] = function (data) + -- ECC / SLS + local dsn = string.byte(data, 2) + -- No PSN + local data = string.sub(data, 3) + local data_lsb = string.sub(data, 4) + + local full_data = ((data & 15) << 8) | data_lsb + local variant = (data >> 4) & 3 + if variant == 0 then + dsn_helper(dsn, function () + rds.set_ecc(full_data) + end) + end + + return 4 +end +mec_handlers[0x19] = function (data) + -- CT + local data = string.byte(data, 2) + rds.set_ct(data ~= 0) + return 2 +end +mec_handlers[0x1C] = function (data) + -- DS select + local dsn = string.byte(data, 2) + dp.set_program(dsn) + dp.set_writing_program(dsn) + return 2 +end + +---@param data string +local function undo_byte_stuff(data) + local output = {} + local i = 1 + while i <= #data do + local d0 = string.byte(data, i) + if d0 == 0xFD then + local d1 = string.byte(data, i + 1) + if not d1 then error("truncated escape sequence") end + output[#output + 1] = string.char(0xFD + d1) + i = i + 2 + else + output[#output + 1] = string.char(d0) + i = i + 1 + end + end + return table.concat(output) +end + +---@param packet string +function uecp.parse_uecp(packet) + local end_i = string.find(packet, "\xff", 1, true) + if not end_i then + print("could not find 0xff") + return + end + + local unstuffed = undo_byte_stuff(string.sub(packet, 2, end_i - 1)) + + local addr1 = string.byte(unstuffed, 1) + local addr2 = string.byte(unstuffed, 2) + local site_addr = (addr1 << 2) | (addr2 >> 6) + local encoder_addr = addr2 & 0x3F + local sequence_count = string.byte(unstuffed, 3) + local mfl = string.byte(unstuffed, 4) + + local data = string.sub(unstuffed, 5, 4 + mfl) + + if mfl ~= #data then + print("data len not right") + return + end + + local crc_calculated = dp.crc16(string.sub(unstuffed, 1, 4 + mfl)) + local crc_hi = string.byte(unstuffed, 4 + mfl + 1) + local crc_lo = string.byte(unstuffed, 4 + mfl + 2) + local crc_stored = (crc_hi << 8) | crc_lo + + if crc_calculated ~= crc_stored then + print("bad crc") + return + end + + local consumed = 1 + while consumed <= #data do + local mec = string.byte(data, consumed) + local handler = mec_handlers[mec] + if not handler then + print(string.format("unknown MEC 0x%02X, cannot continue", mec)) + break + end + local advance = handler(string.sub(data, consumed)) + consumed = consumed + advance + dp.set_writing_program(dp.get_program()) + end +end \ No newline at end of file diff --git a/scripts/99-data.lua b/scripts/99-data.lua index 195c5ba..f871c2e 100644 --- a/scripts/99-data.lua +++ b/scripts/99-data.lua @@ -1,121 +1,6 @@ -local mec_handlers = {} - -local function dsn_helper(dsn, write) - if dsn == 0 then write() - elseif dsn == 254 then - local start = dp.get_writing_program() - for i = 1, dp.max_programs do - local p = i - 1 - if p ~= start then - dp.set_writing_program(p) - write() - end - end - elseif dsn == 255 then - for i = 1, dp.max_programs do - dp.set_writing_program(i - 1) - write() - end - else - dp.set_writing_program(dsn - 1) - write() - end -end - -mec_handlers[1] = function (data) - -- PI - local dsn = string.byte(data, 2) - local psn = string.byte(data, 3) - local pi_msb = string.byte(data, 4) - local pi_lsb = string.byte(data, 5) - - dsn_helper(dsn, function () - rds.set_pi((pi_msb << 8) | pi_lsb) - end) -end -mec_handlers[2] = function (data) - -- PS - local dsn = string.byte(data, 2) - local psn = string.byte(data, 3) - local ps = string.sub(data, 4, 11) - dsn_helper(dsn, function () - rds.set_ps(ps) - end) -end -mec_handlers[3] = function (data) - -- TP/TA - local dsn = string.byte(data, 2) - local psn = string.byte(data, 3) - local data = string.byte(data, 4) - dsn_helper(dsn, function () - rds.set_ta((data & 1) ~= 0) - rds.set_ta((data & 2) ~= 0) - end) -end - ----@param data string -local function undo_byte_stuff(data) - local output = {} - local i = 1 - while i <= #data do - local d0 = string.byte(data, i) - if d0 == 0xFD then - local d1 = string.byte(data, i + 1) - output[#output + 1] = string.char(0xFD + d1) - i = i + 2 - else - output[#output + 1] = string.char(d0) - i = i + 1 - end - end - return table.concat(output) -end - ----@param packet string -local function parse_uecp(packet) - local end_i = string.find(packet, "\xff", 1, true) - if not end_i then - print("could not find 0xff") - return - end - - local unstuffed = undo_byte_stuff(string.sub(packet, 2, end_i - 1)) - - local addr1 = string.byte(unstuffed, 1) - local addr2 = string.byte(unstuffed, 2) - local site_addr = (addr1 << 2) | (addr2 >> 6) - local encoder_addr = addr2 & 0x3F - local sequence_count = string.byte(unstuffed, 3) - local mfl = string.byte(unstuffed, 4) - - local data = string.sub(unstuffed, 5, 4 + mfl) - - if mfl ~= #data then - print("data len not right") - return - end - - local crc_calculated = dp.crc16(string.sub(unstuffed, 1, 4 + mfl)) - local crc_hi = string.byte(unstuffed, 4 + mfl + 1) - local crc_lo = string.byte(unstuffed, 4 + mfl + 2) - local crc_stored = (crc_hi << 8) | crc_lo - - if crc_calculated ~= crc_stored then - print("bad crc") - return - end - - local mec = string.byte(data, 1) - local handler = mec_handlers[mec] - if handler then - handler(data) - dp.set_writing_program(dp.get_program()) - end -end - function data_handle(data) -- UECP - if string.byte(data, 1) == 0xfe then return parse_uecp(data) end + if uecp.parse_uecp and string.byte(data, 1) == 0xfe then return uecp.parse_uecp(data) end if string.sub(data, 1, 4):lower() == "lua=" then local chunk, err = load("return " .. string.sub(data, 5), "udp_chunk") -- skip "lua=" @@ -171,10 +56,8 @@ function data_handle(data) elseif data == "dpty" then return string.format("DPTY=%s\r\n", string.format("%d", (rds.get_dpty() and 1 or 0))) elseif data == "tp" then return string.format("TP=%s\r\n", string.format("%d", (rds.get_tp() and 1 or 0))) elseif data == "ta" then return string.format("TA=%s\r\n", string.format("%d", (rds.get_ta() and 1 or 0))) - elseif data == "rt1en" then return string.format("RT1EN=%s\r\n", string.format("%d", (rds.get_rt1_enabled() and 1 or 0))) - elseif data == "rt2en" then return string.format("RT2EN=%s\r\n", string.format("%d", (rds.get_rt2_enabled() and 1 or 0))) + elseif data == "rt1en" then return string.format("RT1EN=%s\r\n", string.format("%d", (rds.get_rt_enabled() and 1 or 0))) elseif data == "ptynen" then return string.format("PTYNEN=%s\r\n", string.format("%d", (rds.get_ptyn_enabled() and 1 or 0))) - elseif data == "rttype" then return string.format("RTTYPE=%s\r\n", string.format("%d", rds.get_rt_type())) elseif data == "rds2mod" then return string.format("RDS2MOD=%s\r\n", string.format("%d", rds.get_rds2_mode())) elseif data == "rdsgen" then return string.format("RDSGEN=%s\r\n", string.format("%d", rds.get_streams())) elseif data == "link" then return string.format("LINK=%s\r\n", string.format("%d", (rds.get_link() and 1 or 0))) @@ -353,23 +236,13 @@ function data_handle(data) elseif cmd == "rt1en" then local en = tonumber(value) if not en then return "-\r\n" end - rds.set_rt1_enabled(en ~= 0) - return "+\r\n" - elseif cmd == "rt2en" then - local en = tonumber(value) - if not en then return "-\r\n" end - rds.set_rt2_enabled(en ~= 0) + rds.set_rt_enabled(en ~= 0) return "+\r\n" elseif cmd == "ptynen" then local en = tonumber(value) if not en then return "-\r\n" end rds.set_ptyn_enabled(en ~= 0) return "+\r\n" - elseif cmd == "rttype" then - local type = tonumber(value) - if not type then return "-\r\n" end - rds.set_rt_type(type) - return "+\r\n" elseif cmd == "rds2mod" then local type = tonumber(value) if not type then return "-\r\n" end @@ -390,10 +263,13 @@ function data_handle(data) rds.set_tps(value) return "+\r\n" elseif cmd == "rt1" or cmd == "text" then - rds.set_rt1(value) - return "+\r\n" - elseif cmd == "rt2" then - rds.set_rt2(value) + uecp.rt_buffer = {} + uecp.rt_buffer_index = 1 + uecp.rt_buffer[1] = { text = value, number_tx = 0, toggle_ab = true } + uecp.rt_tx_remaining = 0 + rds.set_rt_enabled(true) + rds.set_rt(value) + rds.toggle_rt_ab() return "+\r\n" elseif cmd == "lps" then rds.set_lps(value) @@ -406,11 +282,6 @@ function data_handle(data) if not link then return "-\r\n" end rds.set_link(link ~= 0) return "+\r\n" - elseif cmd == "rtper" then - local period = tonumber(value) - if not period then return "-\r\n" end - rds.set_rt_switching_period(period*60) - return "+\r\n" elseif cmd == "program" then local program = tonumber(value) if not program then return "-\r\n" end @@ -419,11 +290,6 @@ function data_handle(data) dp.set_writing_program(program-1) rds.set_ta(false) return "+\r\n" - elseif cmd == "dttmout" then - local timeout = tonumber(value) - if not timeout then return "-\r\n" end - rds.set_rt_text_timeout(timeout*60) - return "+\r\n" elseif cmd == "grpseq" then rds.set_grpseq(value) return "+\r\n" diff --git a/src/lua_api.c b/src/lua_api.c index d713790..fe4ed83 100644 --- a/src/lua_api.c +++ b/src/lua_api.c @@ -162,18 +162,12 @@ BOOL_GETTER(tp) BOOL_SETTER(ta) BOOL_GETTER(ta) -BOOL_SETTER(rt1_enabled) -BOOL_GETTER(rt1_enabled) - -BOOL_SETTER(rt2_enabled) -BOOL_GETTER(rt2_enabled) +BOOL_SETTER(rt_enabled) +BOOL_GETTER(rt_enabled) BOOL_SETTER(ptyn_enabled) BOOL_GETTER(ptyn_enabled) -INT_SETTER(rt_type) -INT_GETTER(rt_type) - int lua_set_rds2_mode(lua_State *localL) { enc->encoder_data.rds2_mode = luaL_checkinteger(localL, 1); return 0; @@ -238,20 +232,6 @@ int lua_get_rds_writing_program(lua_State *localL) { return 1; } -int lua_set_rds_rt_switching_period(lua_State *localL) { - enc->data[writing_program].rt_switching_period = luaL_checkinteger(localL, 1); - enc->state[writing_program].rt_switching_period_state = enc->data[writing_program].rt_switching_period; - return 0; -} -INT_GETTER(rt_switching_period) - -int lua_set_rds_rt_text_timeout(lua_State *localL) { - enc->data[writing_program].rt_text_timeout = luaL_checkinteger(localL, 1); - enc->state[writing_program].rt_text_timeout_state = enc->data[writing_program].rt_text_timeout; - return 0; -} -INT_GETTER(rt_text_timeout) - int lua_put_rds_custom_group(lua_State *localL) { enc->state[writing_program].custom_group[0] = 1; enc->state[writing_program].custom_group[1] = luaL_checkinteger(localL, 1); @@ -272,9 +252,7 @@ int lua_put_rds2_custom_group(lua_State *localL) { STR_SETTER(ptyn, set_rds_ptyn) STR_SETTER(ps, set_rds_ps) STR_SETTER(tps, set_rds_tps) -STR_SETTER(rt1, set_rds_rt1) -STR_SETTER(rt2, set_rds_rt2) -STR_SETTER(default_rt, set_rds_default_rt) +STR_SETTER(rt, set_rds_rt) STR_RAW_SETTER(lps, set_rds_lps) STR_RAW_GETTER(lps, LPS_LENGTH) @@ -293,6 +271,11 @@ STR_RAW_GETTER(grp_sqc, 32) AF_SETTER(af_group0, af, RDSAFs, add_rds_af) +int lua_toggle_rt_ab(lua_State *localL) { + TOGGLE(enc->state[writing_program].rt_ab); + return 0; +} + int lua_set_rds_eon(lua_State *localL) { int eon = luaL_checkinteger(localL, 1); if(eon >= EONs) return luaL_error(localL, "eon index exceeded"); diff --git a/src/lua_api.h b/src/lua_api.h index 28f00e9..0c0e785 100644 --- a/src/lua_api.h +++ b/src/lua_api.h @@ -38,18 +38,12 @@ int lua_get_rds_tp(lua_State *localL); int lua_set_rds_ta(lua_State *localL); int lua_get_rds_ta(lua_State *localL); -int lua_set_rds_rt1_enabled(lua_State *localL); -int lua_get_rds_rt1_enabled(lua_State *localL); - -int lua_set_rds_rt2_enabled(lua_State *localL); -int lua_get_rds_rt2_enabled(lua_State *localL); +int lua_set_rds_rt_enabled(lua_State *localL); +int lua_get_rds_rt_enabled(lua_State *localL); int lua_set_rds_ptyn_enabled(lua_State *localL); int lua_get_rds_ptyn_enabled(lua_State *localL); -int lua_set_rds_rt_type(lua_State *localL); -int lua_get_rds_rt_type(lua_State *localL); - int lua_set_rds2_mode(lua_State *localL); int lua_get_rds2_mode(lua_State *localL); @@ -71,26 +65,20 @@ int lua_get_rds_program(lua_State *localL); int lua_set_rds_writing_program(lua_State *localL); int lua_get_rds_writing_program(lua_State *localL); -int lua_set_rds_rt_switching_period(lua_State *localL); -int lua_get_rds_rt_switching_period(lua_State *localL); - -int lua_set_rds_rt_text_timeout(lua_State *localL); -int lua_get_rds_rt_text_timeout(lua_State *localL); - int lua_put_rds_custom_group(lua_State *localL); int lua_put_rds2_custom_group(lua_State *localL); int lua_set_rds_lps(lua_State *localL); int lua_get_rds_lps(lua_State *localL); int lua_set_rds_af_group0(lua_State *localL); -int lua_set_rds_default_rt(lua_State *localL); -int lua_set_rds_rt2(lua_State *localL); -int lua_set_rds_rt1(lua_State *localL); +int lua_set_rds_rt(lua_State *localL); int lua_set_rds_tps(lua_State *localL); int lua_set_rds_ps(lua_State *localL); int lua_set_rds_ptyn(lua_State *localL); int lua_set_rds_grp_sqc(lua_State *localL); +int lua_toggle_rt_ab(lua_State *localL); + int lua_set_rds_eon(lua_State *localL); int lua_get_rds_eon(lua_State *localL); int lua_set_rds_udg(lua_State *localL); diff --git a/src/lua_rds.c b/src/lua_rds.c index f670a64..1a663b6 100644 --- a/src/lua_rds.c +++ b/src/lua_rds.c @@ -92,36 +92,23 @@ void init_lua(RDSEncoder* _enc) { lua_registertotable(L, "set_ta", lua_set_rds_ta); lua_registertotable(L, "get_ta", lua_get_rds_ta); - lua_registertotable(L, "set_rt1_enabled", lua_set_rds_rt1_enabled); - lua_registertotable(L, "get_rt1_enabled", lua_get_rds_rt1_enabled); - - lua_registertotable(L, "set_rt2_enabled", lua_set_rds_rt2_enabled); - lua_registertotable(L, "get_rt2_enabled", lua_get_rds_rt2_enabled); + lua_registertotable(L, "set_rt_enabled", lua_set_rds_rt_enabled); + lua_registertotable(L, "get_rt_enabled", lua_get_rds_rt_enabled); lua_registertotable(L, "set_ptyn_enabled", lua_set_rds_ptyn_enabled); lua_registertotable(L, "get_ptyn_enabled", lua_get_rds_ptyn_enabled); - lua_registertotable(L, "set_rt_type", lua_set_rds_rt_type); - lua_registertotable(L, "get_rt_type", lua_get_rds_rt_type); - lua_registertotable(L, "set_rds2_mode", lua_set_rds2_mode); lua_registertotable(L, "get_rds2_mode", lua_get_rds2_mode); lua_registertotable(L, "set_link", lua_set_rds_link); lua_registertotable(L, "get_link", lua_get_rds_link); - lua_registertotable(L, "set_rt_switching_period", lua_set_rds_rt_switching_period); - lua_registertotable(L, "get_rt_switching_period", lua_get_rds_rt_switching_period); - - lua_registertotable(L, "set_rt_text_timeout", lua_set_rds_rt_text_timeout); - lua_registertotable(L, "get_rt_text_timeout", lua_get_rds_rt_text_timeout); - lua_registertotable(L, "set_ptyn", lua_set_rds_ptyn); lua_registertotable(L, "set_ps", lua_set_rds_ps); lua_registertotable(L, "set_tps", lua_set_rds_tps); - lua_registertotable(L, "set_rt1", lua_set_rds_rt1); - lua_registertotable(L, "set_rt2", lua_set_rds_rt2); - lua_registertotable(L, "set_default_rt", lua_set_rds_default_rt); + lua_registertotable(L, "set_rt", lua_set_rds_rt); + lua_registertotable(L, "toggle_rt_ab", lua_toggle_rt_ab); lua_registertotable(L, "set_lps", lua_set_rds_lps); lua_registertotable(L, "get_lps", lua_get_rds_lps); @@ -362,10 +349,29 @@ void lua_call_table(const char* function) { } void lua_call_tfunction_nolock(const char* name) { - char table_name[256]; - lua_call_function_nolock(name); - snprintf(table_name, sizeof(table_name), "%ss", name); - lua_call_table_nolock(table_name); + lua_getglobal(L, "hooks"); + lua_getfield(L, -1, name); + + if (!lua_istable(L, -1)) { + lua_pop(L, 2); + return; + } + + lua_Integer len = lua_rawlen(L, -1); + for (lua_Integer i = 1; i <= len; i++) { + lua_rawgeti(L, -1, i); + if (lua_isfunction(L, -1)) { + if (lua_pcall(L, 0, 0, 0) != LUA_OK) { + fprintf(stderr, + "Lua error: %s at '%s[%lld]'\n", + lua_tostring(L, -1), + name, + (long long)i); + lua_pop(L, 1); + } + } else lua_pop(L, 1); + } + lua_pop(L, 2); // pop table } void lua_call_tfunction(const char* name) { diff --git a/src/rds.c b/src/rds.c index f28f768..6df024e 100644 --- a/src/rds.c +++ b/src/rds.c @@ -101,7 +101,7 @@ static uint8_t check_rds_good_group(RDSEncoder* enc, char* grp) { uint8_t good_group = 0; if(*grp == '0') good_group = 1; if(*grp == '1' && (enc->data[enc->program].ecc != 0 || enc->data[enc->program].slc_data != 0)) good_group = 1; - if(*grp == '2' && (enc->data[enc->program].rt1_enabled || enc->data[enc->program].rt2_enabled)) good_group = 1; + if(*grp == '2' && enc->data[enc->program].rt_enabled) good_group = 1; if(*grp == 'A' && enc->data[enc->program].ptyn_enabled) good_group = 1; if(*grp == 'E') { for (int i = 0; i < EONs; i++) { @@ -133,28 +133,6 @@ void get_rds_group(RDSEncoder* enc, RDSGroup *group, uint8_t stream) { if(utc->tm_sec != enc->state[enc->program].last_second) { enc->state[enc->program].last_second = utc->tm_sec; - - if(enc->data[enc->program].rt1_enabled && enc->data[enc->program].current_rt == 0 && enc->state[enc->program].rt_text_timeout_state != 0) { - enc->state[enc->program].rt_text_timeout_state--; - if(enc->state[enc->program].rt_text_timeout_state == 0) { - enc->state[enc->program].rt_update = 1; - memset(enc->state[enc->program].rt_text, 0, RT_LENGTH); - memcpy(enc->state[enc->program].rt_text, enc->data[enc->program].default_rt, RT_LENGTH); - enc->state[enc->program].rt_segments = enc->state[enc->program].default_rt_segments; - } - } - if(enc->data[enc->program].rt1_enabled && enc->data[enc->program].rt2_enabled && enc->state[enc->program].rt_switching_period_state != 0) { - enc->state[enc->program].rt_switching_period_state--; - if(enc->state[enc->program].rt_switching_period_state == 0) { - TOGGLE(enc->data[enc->program].current_rt); - memset(enc->state[enc->program].rt_text, 0, RT_LENGTH); - if (enc->data[enc->program].current_rt == 1) memcpy(enc->state[enc->program].rt_text, enc->data[enc->program].rt2, RT_LENGTH); - else memcpy(enc->state[enc->program].rt_text, enc->data[enc->program].rt1, RT_LENGTH); - enc->state[enc->program].rt_state = 0; - enc->state[enc->program].rt_switching_period_state = enc->data[enc->program].rt_switching_period; - } - } - lua_call_tfunction("tick"); } @@ -327,10 +305,8 @@ void reset_rds_state(RDSEncoder* enc, uint8_t program) { tempCoder.program = program; memset(&tempCoder.state[program], 0, sizeof(RDSState)); - tempCoder.state[program].rt_ab = 1; tempCoder.state[program].ptyn_ab = 1; - set_rds_rt1(&tempCoder, enc->data[program].rt1, program); - set_rds_rt2(&tempCoder, enc->data[program].rt2, program); + set_rds_rt(&tempCoder, enc->data[program].rt, program); set_rds_ps(&tempCoder, enc->data[program].ps, program); set_rds_tps(&tempCoder, enc->data[program].tps, program); set_rds_lps(&tempCoder, enc->data[program].lps, program); @@ -360,13 +336,9 @@ void set_rds_defaults(RDSEncoder* enc, uint8_t program) { enc->data[program].tp = 1; enc->data[program].pi = 0xFFFF; strcpy((char *)enc->data[program].ps, "* RDS * "); - enc->data[program].rt1_enabled = 1; + enc->data[program].rt_enabled = 1; - memset(enc->data[program].rt1, ' ', 64); - memset(enc->data[program].rt2, ' ', 64); - memset(enc->data[program].default_rt, ' ', 64); - - enc->data[program].rt_type = 2; + memset(enc->data[program].rt, ' ', 64); reset_rds_state(enc, program); } diff --git a/src/rds.h b/src/rds.h index baa759c..9eec9e6 100644 --- a/src/rds.h +++ b/src/rds.h @@ -46,7 +46,7 @@ typedef struct { uint16_t pi; char ps[PS_LENGTH]; - char rt1[RT_LENGTH]; + char rt[RT_LENGTH]; uint8_t ecc; uint16_t slc_data : 12; @@ -58,14 +58,7 @@ typedef struct { char tps[PS_LENGTH]; - uint8_t rt1_enabled : 1; - uint8_t rt2_enabled : 1; - uint8_t rt_type : 2; - uint8_t rt_text_timeout; - uint8_t rt_switching_period; - uint8_t current_rt : 1; - char default_rt[RT_LENGTH]; - char rt2[RT_LENGTH]; + uint8_t rt_enabled : 1; uint8_t ptyn_enabled : 1; char ptyn[PTYN_LENGTH]; @@ -108,11 +101,8 @@ typedef struct { char rt_text[RT_LENGTH]; uint8_t rt_state : 5; uint8_t rt_update : 1; - uint8_t rt2_update : 1; uint8_t rt_ab : 1; uint8_t rt_segments : 5; - uint8_t rt2_segments : 5; - uint8_t default_rt_segments : 5; char ptyn_text[PTYN_LENGTH]; uint8_t ptyn_state : 1; @@ -127,9 +117,6 @@ typedef struct { uint16_t custom_group[GROUP_LENGTH]; uint16_t custom_group2[GROUP_LENGTH + 1]; - uint8_t rt_switching_period_state; - uint8_t rt_text_timeout_state; - uint8_t data_ecc : 1; uint8_t grp_seq_idx[4]; uint8_t udg_idxs[2]; @@ -195,9 +182,7 @@ void add_checkwords(RDSGroup *group, uint8_t *bits); void get_rds_group(RDSEncoder* enc, RDSGroup *group, uint8_t stream); void get_rds_bits(RDSEncoder* enc, uint8_t *bits, uint8_t stream); -void set_rds_rt1(RDSEncoder* enc, const char *rt1, uint8_t program); -void set_rds_rt2(RDSEncoder* enc, const char *rt2, uint8_t program); -void set_rds_default_rt(RDSEncoder* enc, const char *rt, uint8_t program); +void set_rds_rt(RDSEncoder* enc, const char *rt, uint8_t program); void set_rds_ps(RDSEncoder* enc, const char *ps, uint8_t program); void set_rds_tps(RDSEncoder* enc, const char *tps, uint8_t program); void set_rds_lps(RDSEncoder* enc, const char *lps, uint8_t program); diff --git a/src/rds_groups.c b/src/rds_groups.c index 3c0c4d2..15f23dc 100644 --- a/src/rds_groups.c +++ b/src/rds_groups.c @@ -87,44 +87,25 @@ void get_rds_fasttuning_group(RDSEncoder* enc, RDSGroup *group) { } void get_rds_rt_group(RDSEncoder* enc, RDSGroup *group) { - if (enc->state[enc->program].rt_update && enc->data[enc->program].rt1_enabled && enc->data[enc->program].current_rt == 0) { - memcpy(enc->state[enc->program].rt_text, enc->data[enc->program].rt1, RT_LENGTH); - TOGGLE(enc->state[enc->program].rt_ab); + if (enc->state[enc->program].rt_update && enc->data[enc->program].rt_enabled) { + memcpy(enc->state[enc->program].rt_text, enc->data[enc->program].rt, RT_LENGTH); enc->state[enc->program].rt_update = 0; enc->state[enc->program].rt_state = 0; - enc->data[enc->program].current_rt = 0; - } - if(enc->state[enc->program].rt2_update && enc->data[enc->program].rt2_enabled && enc->data[enc->program].current_rt) { - memcpy(enc->state[enc->program].rt_text, enc->data[enc->program].rt2, RT_LENGTH); - TOGGLE(enc->state[enc->program].rt_ab); - enc->state[enc->program].rt2_update = 0; - enc->state[enc->program].rt_state = 0; - enc->data[enc->program].current_rt = 1; - } - - uint8_t ab = enc->state[enc->program].rt_ab; - switch (enc->data[enc->program].rt_type) - { - case 0: - ab = 0; - break; - case 1: - ab = (enc->data[enc->program].current_rt == 0) ? 0 : 1; - break; - default: break; } group->b |= 2 << 12; - group->b |= ab << 4; + group->b |= enc->state[enc->program].rt_ab << 4; group->b |= enc->state[enc->program].rt_state; - group->c = enc->state[enc->program].rt_text[enc->state[enc->program].rt_state * 4 ] << 8; + group->c = enc->state[enc->program].rt_text[enc->state[enc->program].rt_state * 4] << 8; group->c |= enc->state[enc->program].rt_text[enc->state[enc->program].rt_state * 4 + 1]; group->d = enc->state[enc->program].rt_text[enc->state[enc->program].rt_state * 4 + 2] << 8; group->d |= enc->state[enc->program].rt_text[enc->state[enc->program].rt_state * 4 + 3]; - uint8_t segments = (enc->data[enc->program].current_rt == 1) ? enc->state[enc->program].rt2_segments : enc->state[enc->program].rt_segments; enc->state[enc->program].rt_state++; - if (enc->state[enc->program].rt_state >= segments || enc->state[enc->program].rt_state >= 16) enc->state[enc->program].rt_state = 0; + if (enc->state[enc->program].rt_state >= enc->state[enc->program].rt_segments || enc->state[enc->program].rt_state >= 16) { + lua_call_tfunction("rt_transmission"); + enc->state[enc->program].rt_state = 0; + } } void get_rdsp_ct_group(RDSGroup *group, time_t now) { diff --git a/src/rds_setters.c b/src/rds_setters.c index 17a3308..d6e433e 100644 --- a/src/rds_setters.c +++ b/src/rds_setters.c @@ -1,20 +1,18 @@ #include "rds.h" -void set_rds_rt1(RDSEncoder* enc, const char *rt1, uint8_t program) { +void set_rds_rt(RDSEncoder* enc, const char *rt, uint8_t program) { uint8_t i = 0, len = 0; - enc->state[program].rt_text_timeout_state = enc->data[program].rt_text_timeout; - enc->state[program].rt_update = 1; - memset(enc->data[program].rt1, ' ', RT_LENGTH); - while (*rt1 != 0 && len < RT_LENGTH) enc->data[program].rt1[len++] = *rt1++; + memset(enc->data[program].rt, ' ', RT_LENGTH); + while (*rt != 0 && len < RT_LENGTH) enc->data[program].rt[len++] = *rt++; - while (len > 0 && enc->data[program].rt1[len - 1] == ' ') len--; + while (len > 0 && enc->data[program].rt[len - 1] == ' ') len--; if (len < RT_LENGTH) { enc->state[program].rt_segments = 0; - enc->data[program].rt1[len++] = '\r'; + enc->data[program].rt[len++] = '\r'; while (i < len) { i += 4; enc->state[program].rt_segments++; @@ -23,46 +21,6 @@ void set_rds_rt1(RDSEncoder* enc, const char *rt1, uint8_t program) { if(enc->state[program].rt_segments > 16) enc->state[program].rt_segments = 16; //make sure } -void set_rds_default_rt(RDSEncoder* enc, const char *rt, uint8_t program) { - uint8_t i = 0, len = 0; - - memset(enc->data[program].default_rt, ' ', RT_LENGTH); - while (*rt != 0 && len < RT_LENGTH) enc->data[program].default_rt[len++] = *rt++; - - while (len > 0 && enc->data[program].default_rt[len - 1] == ' ') len--; - - if (len < RT_LENGTH) { - enc->state[program].default_rt_segments = 0; - enc->data[program].default_rt[len++] = '\r'; - while (i < len) { - i += 4; - enc->state[program].default_rt_segments++; - } - } else enc->state[program].default_rt_segments = 16; - if(enc->state[program].default_rt_segments > 16) enc->state[program].default_rt_segments = 16; //make sure -} - -void set_rds_rt2(RDSEncoder* enc, const char *rt2, uint8_t program) { - uint8_t i = 0, len = 0; - - enc->state[program].rt2_update = 1; - - memset(enc->data[program].rt2, ' ', RT_LENGTH); - while (*rt2 != 0 && len < RT_LENGTH) enc->data[program].rt2[len++] = *rt2++; - - while (len > 0 && enc->data[program].rt2[len - 1] == ' ') len--; - - if (len < RT_LENGTH) { - enc->state[program].rt2_segments = 0; - enc->data[program].rt2[len++] = '\r'; - while (i < len) { - i += 4; - enc->state[program].rt2_segments++; - } - } else enc->state[program].rt2_segments = 16; - if(enc->state[program].rt2_segments > 16) enc->state[program].rt2_segments = 16; //make sure -} - void set_rds_ps(RDSEncoder* enc, const char *ps, uint8_t program) { uint8_t len = 0; @@ -93,7 +51,7 @@ void set_rds_lps(RDSEncoder* enc, const char *lps, uint8_t program) { return; } memset(enc->data[program].lps, ' ', LPS_LENGTH); - while (*lps != 0 && len < LPS_LENGTH) enc->data[program].lps[len++] = *lps++; + while (*lps != '\r' && *lps != 0 && len < LPS_LENGTH) enc->data[program].lps[len++] = *lps++; if (len < LPS_LENGTH) { enc->state[program].lps_segments = 0;