diff --git a/plugin.lua b/plugin.lua index 60b5835..c251a77 100644 --- a/plugin.lua +++ b/plugin.lua @@ -12,6 +12,11 @@ user_data_len = 0 --#region Functions implemented or used in C +---Executes a CRC-16 CCIIT +---@param data string +---@return integer +function crc16(data) end + ---Starts the initialization sequence, also calls the on_init function ---@return nil function set_rds_program_defaults() end diff --git a/scripts/0-rds2_oda.lua b/scripts/0-rds2_oda.lua index f57869a..fb8d48b 100644 --- a/scripts/0-rds2_oda.lua +++ b/scripts/0-rds2_oda.lua @@ -44,6 +44,7 @@ function rds2_group(stream) local oda = _RDS2_ODAs[_RDS2_ODA_pointer] local channel_offset = 16 * ((not oda.file_related) and 1 or 0) local channel = ((_RDS2_ODA_pointer - 1 + channel_offset) & 0x3F) + if oda.file_related then channel = channel & 0xF end _RDS2_ODA_pointer = _RDS2_ODA_pointer + 1 @@ -78,9 +79,11 @@ function rds2_group(stream) 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 - elseif fid == 0 and fn_msb == 1 then channel = channel & 0xF + if fid == 0 and fn_msb == 0 then + warn("RDS2 ODA is tunneling (A or B) over C") + return true, 0, b, c, d -- Tunnel, not sure why but sure --FID = 1 means a normal ODA group + --FID = 2 means a RFT file 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 diff --git a/scripts/1-rft.lua b/scripts/1-rft.lua index 7377a6d..6f82f98 100644 --- a/scripts/1-rft.lua +++ b/scripts/1-rft.lua @@ -3,11 +3,15 @@ _Rft_file = "" _Rft_file_segment = 0 _Rft_toggle = false _Rft_last_id = -1 -_Rft_version = 0 -- TODO +_Rft_version = 0 +_Rft_crc_mode = -1 +_Rft_crc_state = 0 +_Rft_crc_sent = false +_Rft_aid = 0xFF7F local function start_rft() if _Rft_oda_id == nil then - _Rft_oda_id = register_oda_rds2(0xFF7F, 0, true) + _Rft_oda_id = register_oda_rds2(_Rft_aid, 0, true) set_oda_handler_rds2(_Rft_oda_id, function () if #_Rft_file == 0 then return false, 0, 0, 0, 0 end @@ -15,6 +19,14 @@ local function start_rft() local seg = _Rft_file_segment local base = seg * 5 + 1 + if not _Rft_crc_sent and _Rft_crc_mode ~= -1 then + --- TODO: warn that if we have over 511 segments, we can't have CRC if we want per-segment crc + _Rft_crc_sent = true + if _Rft_crc_mode ~= 0 then warn("rft: No other crc than mode 0 is supported as of now") end + _Rft_crc_mode = 0 + return true, (2 << 14), _Rft_aid, (1 << 28) | (_Rft_crc_mode & 7) << 25 | (seg & 511), crc16(_Rft_file) + end + local function b(i) return string.byte(_Rft_file, base + i) or 0 end local word1 = (((_Rft_toggle and 1 or 0) << 7) | ((seg >> 8) & 0x7F)) @@ -23,9 +35,7 @@ local function start_rft() local word4 = (b(3) << 8) | b(4) _Rft_file_segment = seg + 1 - if _Rft_file_segment >= total_segments then - _Rft_file_segment = 0 - end + if _Rft_file_segment >= total_segments then _Rft_file_segment = 0 end return true, (2 << 12) | word1, word2, word3, word4 end) @@ -36,7 +46,8 @@ end ---Loads the file into RFT and initializes it if needed, note that this needs RDR2 mode 2 ---@param path string ---@param id integer -function load_station_logo(path, id) +---@param crc boolean +function load_station_logo(path, id, crc) local file = io.open(path, "rb") if not file then error("Could not open file") end _Rft_file = file:read("*a") @@ -44,13 +55,21 @@ function load_station_logo(path, id) if id == _Rft_last_id then _Rft_toggle = not _Rft_toggle + _Rft_crc_state = 0 + _Rft_crc_sent = false _Rft_version = _Rft_version + 1 if _Rft_version > 7 then _Rft_version = 0 end end + if crc then + _Rft_crc_mode = 0 + else + _Rft_crc_mode = -1 + end + if #_Rft_file > 262143 then error("The file is too large", 2) end if _Rft_oda_id == nil then start_rft() end ---@diagnostic disable-next-line: param-type-mismatch - set_oda_id_data_rds2(_Rft_oda_id, #_Rft_file | (id & 63) << 18 | (_Rft_version & 7) << 24 | 0 << 27) + set_oda_id_data_rds2(_Rft_oda_id, #_Rft_file | (id & 63) << 18 | (_Rft_version & 7) << 24 | ((_Rft_crc_mode ~= -1) and 1 or 0) << 27) _Rft_last_id = id end \ No newline at end of file diff --git a/src/common.h b/src/common.h index aacb924..acd37b9 100644 --- a/src/common.h +++ b/src/common.h @@ -4,9 +4,7 @@ #include #include #include -#ifndef _WIN32 #include -#endif #include #include #include diff --git a/src/lib.c b/src/lib.c index 0a19fe9..fa1b2f5 100644 --- a/src/lib.c +++ b/src/lib.c @@ -25,7 +25,7 @@ inline int _strncpy(char *dest, const char *src, int n) { } return i; } -uint16_t crc16_ccitt(char* data, uint16_t len) { +uint16_t crc16_ccitt(const char* data, uint16_t len) { uint16_t i, crc=0xFFFF; for (i=0; i < len; i++ ) { crc = (unsigned char)(crc >> 8) | (crc << 8); diff --git a/src/lib.h b/src/lib.h index 29a9dfd..009d9b8 100644 --- a/src/lib.h +++ b/src/lib.h @@ -8,7 +8,7 @@ void msleep(unsigned long ms); int _strnlen(const char *s, int maxlen); int _strncpy(char *dest, const char *src, int n); -uint16_t crc16_ccitt(char *data, uint16_t len); +uint16_t crc16_ccitt(const char *data, uint16_t len); uint16_t get_block_from_group(RDSGroup *group, uint8_t block); diff --git a/src/lua_rds.c b/src/lua_rds.c index 4ddc529..18d186c 100644 --- a/src/lua_rds.c +++ b/src/lua_rds.c @@ -406,6 +406,13 @@ int lua_get_available_rds_streams(lua_State *localL) { return 1; } +int lua_crc16(lua_State *localL) { + size_t len; + const char* data = luaL_checklstring(localL, 1, &len); + lua_pushinteger(localL, crc16_ccitt(data, len)); + return 1; +} + void init_lua(RDSModulator* rds_mod) { static int mutex_initialized = 0; mod = rds_mod; @@ -524,6 +531,8 @@ void init_lua(RDSModulator* rds_mod) { lua_register(L, "get_userdata", lua_get_userdata); lua_register(L, "get_userdata_offset", lua_get_userdata_offset); + lua_register(L, "crc16", lua_crc16); + if (luaL_loadfile(L, "/etc/rds95.lua") != LUA_OK) { fprintf(stderr, "Lua error loading file: %s\n", lua_tostring(L, -1)); lua_pop(L, 1);