mirror of
https://github.com/radio95-rnt/rds95.git
synced 2026-07-29 16:29:17 +02:00
150 lines
6.0 KiB
Lua
150 lines
6.0 KiB
Lua
local rds2_oda = {}
|
|
|
|
local _RDS2_ODA = { aid = 0, data = 0, handler = false, file_related = false, channel = 0 }
|
|
local _RDS2_next_channel = { normal = 16, file = 0 }
|
|
|
|
function _RDS2_ODA.new(aid, data, handler, file_related, channel)
|
|
local instance = { aid = aid or 0, data = data or 0, handler = handler or false, file_related = file_related or false, channel = channel or 0 }
|
|
setmetatable(instance, { __index = _RDS2_ODA })
|
|
return instance
|
|
end
|
|
|
|
local _RDS2_ODAs = {}
|
|
local _RDS2_ODA_aid = 0
|
|
local _RDS2_ODA_pointer = 1
|
|
|
|
---@param aid integer
|
|
---@param data integer
|
|
---@param file_related boolean
|
|
---@return integer oda_id
|
|
function rds2_oda.register_oda_rds2(aid, data, file_related)
|
|
local channel
|
|
if file_related then
|
|
channel = _RDS2_next_channel.file
|
|
_RDS2_next_channel.file = _RDS2_next_channel.file + 1
|
|
else
|
|
channel = _RDS2_next_channel.normal
|
|
_RDS2_next_channel.normal = _RDS2_next_channel.normal + 1
|
|
end
|
|
|
|
local oda = _RDS2_ODA.new(aid, data, false, file_related, channel)
|
|
for i = 1, #_RDS2_ODAs do
|
|
if _RDS2_ODAs[i] == false then
|
|
_RDS2_ODAs[i] = oda
|
|
return i
|
|
end
|
|
end
|
|
table.insert(_RDS2_ODAs, oda)
|
|
return #_RDS2_ODAs
|
|
end
|
|
|
|
---Unregisters an RDS 2 ODA, this stops the handler or AID being called/sent
|
|
---@param oda_id integer
|
|
function rds2_oda.unregister_oda_rds2(oda_id)
|
|
if oda_id < 1 or oda_id > #_RDS2_ODAs or _RDS2_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end
|
|
|
|
_RDS2_ODAs[oda_id] = false
|
|
|
|
if _RDS2_ODA_pointer == oda_id then _RDS2_ODA_pointer = _RDS2_ODA_pointer + 1 end
|
|
end
|
|
|
|
|
|
---@param oda_id integer
|
|
---@param data integer
|
|
function rds2_oda.set_oda_id_data_rds2(oda_id, data)
|
|
if oda_id < 1 or oda_id > #_RDS2_ODAs or _RDS2_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end
|
|
_RDS2_ODAs[oda_id].data = data
|
|
end
|
|
|
|
---The callback function for an ODA handler
|
|
---@alias RDS2_ODAHandler fun(integer): (boolean, integer, integer, integer, integer)
|
|
|
|
---You are asked to not fill in the channel id in block A, however you are asked to fill in the function number (if you do not know what is that, just OR block A with (1 << 14))
|
|
---@param oda_id integer
|
|
---@param func RDS2_ODAHandler
|
|
function rds2_oda.set_oda_handler_rds2(oda_id, func)
|
|
if oda_id < 1 or oda_id > #_RDS2_ODAs or _RDS2_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end
|
|
_RDS2_ODAs[oda_id].handler = func
|
|
end
|
|
|
|
function rds2_oda.rds2_group(stream)
|
|
if #_RDS2_ODAs == 0 then return false, 0, 0, 0, 0 end
|
|
|
|
if _RDS2_ODA_pointer > #_RDS2_ODAs then _RDS2_ODA_pointer = 1 end
|
|
local checked = 0
|
|
while checked < #_RDS2_ODAs and _RDS2_ODAs[_RDS2_ODA_pointer] == false do
|
|
_RDS2_ODA_pointer = _RDS2_ODA_pointer + 1
|
|
if _RDS2_ODA_pointer > #_RDS2_ODAs then _RDS2_ODA_pointer = 1 end
|
|
checked = checked + 1
|
|
end
|
|
if checked == #_RDS2_ODAs then return false, 0, 0, 0, 0 end
|
|
|
|
local oda = _RDS2_ODAs[_RDS2_ODA_pointer]
|
|
local channel = oda.channel
|
|
if oda.file_related then channel = channel & 0xF end
|
|
|
|
_RDS2_ODA_pointer = _RDS2_ODA_pointer + 1
|
|
|
|
local next_oda = nil
|
|
if _RDS2_ODA_pointer <= #_RDS2_ODAs then next_oda = _RDS2_ODAs[_RDS2_ODA_pointer] end
|
|
|
|
if _RDS2_ODA_aid == 0 and stream == 1 then
|
|
_RDS2_ODA_aid = 1
|
|
local block1_base = (2 << 14) | channel
|
|
|
|
if next_oda and oda.data > 0 and oda.data <= 0xFFFF and next_oda.data == 0 then
|
|
_RDS2_ODA_pointer = _RDS2_ODA_pointer + 1
|
|
return true, block1_base | (1 << 6), oda.aid, oda.data, next_oda.aid
|
|
elseif next_oda and oda.data == 0 and next_oda.data > 0 and next_oda.data <= 0xFFFF then
|
|
_RDS2_ODA_pointer = _RDS2_ODA_pointer + 1
|
|
return true, block1_base | (2 << 6), oda.aid, next_oda.aid, next_oda.data
|
|
elseif next_oda ~= nil and oda.data == 0 and next_oda.data == 0 then
|
|
_RDS2_ODA_pointer = _RDS2_ODA_pointer + 1
|
|
if _RDS2_ODA_pointer <= #_RDS2_ODAs then
|
|
local third_oda = _RDS2_ODAs[_RDS2_ODA_pointer]
|
|
_RDS2_ODA_pointer = _RDS2_ODA_pointer + 1
|
|
return true, block1_base | (3 << 6), oda.aid, next_oda.aid, third_oda.aid
|
|
else return true, block1_base, oda.aid, (oda.data >> 16) & 0xffff, oda.data & 0xffff end
|
|
end
|
|
|
|
return true, block1_base, oda.aid, (oda.data >> 16) & 0xffff, oda.data & 0xffff
|
|
else
|
|
_RDS2_ODA_aid = _RDS2_ODA_aid + 1
|
|
if _RDS2_ODA_aid > 8 then _RDS2_ODA_aid = 0 end
|
|
if oda.handler then
|
|
local generated = false
|
|
checked = 0
|
|
while generated == false and checked < #_RDS2_ODAs do
|
|
local ok, a, b, c, d
|
|
ok, generated, a, b, c, d = pcall(oda.handler, stream)
|
|
if not (generated and ok) then
|
|
_RDS2_ODA_pointer = _RDS2_ODA_pointer + 1
|
|
if _RDS2_ODA_pointer > #_RDS2_ODAs then _RDS2_ODA_pointer = 1 end
|
|
oda = _RDS2_ODAs[_RDS2_ODA_pointer]
|
|
checked = checked + 1
|
|
else
|
|
local channel_bitshift = 8
|
|
local fid = (a & 0xC000) >> 14
|
|
local fn_msb = (a >> 13) & 1
|
|
if fid == 0 and fn_msb == 0 then return true, 0, b, c, d -- Tunnel, not sure why but sure
|
|
-- FID == 0 and FN_MSB = 1 means RFT data, file data
|
|
-- FID == 1 is the standard ODA group
|
|
-- FID == 2 is AID
|
|
-- FID == 3 is reserved
|
|
elseif fid == 2 and fn_msb == 0 then channel_bitshift = 0 end -- This is AID
|
|
return generated, (channel << channel_bitshift) | a, b, c, d
|
|
end
|
|
end
|
|
if checked == #_RDS2_ODAs then return false, 0, 0, 0, 0 end
|
|
end
|
|
return true, (2 << 14) | channel, oda.aid, (oda.data >> 16) & 0xffff, oda.data & 0xffff
|
|
end
|
|
end
|
|
|
|
table.insert(hooks.on_state, function ()
|
|
_RDS2_ODAs = {}
|
|
_RDS2_ODA_aid = 0
|
|
_RDS2_ODA_pointer = 1
|
|
end)
|
|
|
|
return rds2_oda |