mirror of
https://github.com/radio95-rnt/rds95.git
synced 2026-07-29 08:19:17 +02:00
proper uecp
This commit is contained in:
+1
-1
@@ -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)
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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)
|
||||
+1
-1
@@ -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)
|
||||
+1
-1
@@ -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)
|
||||
@@ -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
|
||||
+10
-144
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user