/* Copyright (C) 2020 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "STIDecoder.hpp" #include "buffer_unpack.hpp" #include "crc.h" #include "Log.h" #include #include #include namespace EdiDecoder { using namespace std; STIDecoder::STIDecoder(STIDataCollector& data_collector) : m_data_collector(data_collector), m_dispatcher(std::bind(&STIDecoder::packet_completed, this)) { using std::placeholders::_1; using std::placeholders::_2; m_dispatcher.register_tag("*ptr", std::bind(&STIDecoder::decode_starptr, this, _1, _2)); m_dispatcher.register_tag("dsti", std::bind(&STIDecoder::decode_dsti, this, _1, _2)); m_dispatcher.register_tag("ss", std::bind(&STIDecoder::decode_ssn, this, _1, _2)); m_dispatcher.register_tag("*dmy", std::bind(&STIDecoder::decode_stardmy, this, _1, _2)); m_dispatcher.register_tag("ODRa", std::bind(&STIDecoder::decode_odraudiolevel, this, _1, _2)); m_dispatcher.register_tag("ODRv", std::bind(&STIDecoder::decode_odrversion, this, _1, _2)); } void STIDecoder::set_verbose(bool verbose) { m_dispatcher.set_verbose(verbose); } void STIDecoder::push_bytes(const vector &buf) { m_dispatcher.push_bytes(buf); } void STIDecoder::push_packet(Packet &pack) { m_dispatcher.push_packet(pack); } void STIDecoder::setMaxDelay(int num_af_packets) { m_dispatcher.setMaxDelay(num_af_packets); } #define AFPACKET_HEADER_LEN 10 // includes SYNC bool STIDecoder::decode_starptr(const std::vector& value, const tag_name_t& /*n*/) { if (value.size() != 0x40 / 8) { etiLog.log(warn, "Incorrect length %02lx for *PTR", value.size()); return false; } char protocol_sz[5]; protocol_sz[4] = '\0'; copy(value.begin(), value.begin() + 4, protocol_sz); string protocol(protocol_sz); uint16_t major = read_16b(value.begin() + 4); uint16_t minor = read_16b(value.begin() + 6); m_data_collector.update_protocol(protocol, major, minor); return true; } bool STIDecoder::decode_dsti(const std::vector& value, const tag_name_t& /*n*/) { size_t offset = 0; const uint16_t dstiHeader = read_16b(value.begin() + offset); offset += 2; sti_management_data md; md.stihf = (dstiHeader >> 15) & 0x1; md.atstf = (dstiHeader >> 14) & 0x1; md.rfadf = (dstiHeader >> 13) & 0x1; uint8_t dfcth = (dstiHeader >> 8) & 0x1F; uint8_t dfctl = dstiHeader & 0xFF; md.dlfc = dfcth * 250 + dfctl; // modulo 5000 counter const size_t expected_length = 2 + (md.stihf ? 3 : 0) + (md.atstf ? 1 + 4 + 3 : 0) + (md.rfadf ? 9 : 0); if (value.size() != expected_length) { etiLog.level(warn) << "EDI dsti: decoding error: " << "value.size() != expected_length: " << value.size() << " " << expected_length << " " << (md.stihf ? "STIHF " : " ") << (md.atstf ? "ATSTF " : " ") << (md.rfadf ? "RFADF " : " "); return false; } if (md.stihf) { const uint8_t stat = value[offset++]; const uint16_t spid = read_16b(value.begin() + offset); m_data_collector.update_stat(stat, spid); offset += 2; } if (md.atstf) { uint8_t utco = value[offset]; offset++; uint32_t seconds = read_32b(value.begin() + offset); offset += 4; m_data_collector.update_edi_time(utco, seconds); md.tsta = read_24b(value.begin() + offset); offset += 3; } else { // Null timestamp, ETSI ETS 300 799, C.2.2 md.tsta = 0xFFFFFF; } if (md.rfadf) { std::array rfad; copy(value.cbegin() + offset, value.cbegin() + offset + 9, rfad.begin()); offset += 9; m_data_collector.update_rfad(rfad); } m_data_collector.update_sti_management(md); return true; } bool STIDecoder::decode_ssn(const std::vector& value, const tag_name_t& name) { sti_payload_data sti; uint16_t n = 0; n = (uint16_t)(name[2]) << 8; n |= (uint16_t)(name[3]); sti.stream_index = n - 1; // n is 1-indexed sti.rfa = value[0] >> 3; sti.tid = value[0] & 0x07; uint16_t istc = read_24b(value.begin() + 1); sti.tidext = istc >> 13; sti.crcstf = (istc >> 12) & 0x1; sti.stid = istc & 0xFFF; if (sti.rfa != 0) { etiLog.level(warn) << "EDI: rfa field in SSnn tag non-null"; } copy( value.cbegin() + 3, value.cend(), back_inserter(sti.istd)); m_data_collector.add_payload(move(sti)); return true; } bool STIDecoder::decode_stardmy(const std::vector&, const tag_name_t&) { return true; } bool STIDecoder::decode_odraudiolevel(const std::vector& value, const tag_name_t& /*n*/) { constexpr size_t expected_length = 2 * sizeof(int16_t); audio_level_data audio_level; if (value.size() == expected_length) { audio_level.left = read_16b(value.begin()); audio_level.right = read_16b(value.begin() + 2); } else { audio_level.left = 0; audio_level.right = 0; etiLog.level(warn) << "EDI: ODR AudioLevel TAG has wrong length!"; } m_data_collector.update_audio_levels(audio_level); // Not being able to decode the audio level is a soft-failure, it should // not disrupt decoding the actual audio data. return true; } bool STIDecoder::decode_odrversion(const std::vector& value, const tag_name_t& /*n*/) { const auto vd = parse_odr_version_data(value); m_data_collector.update_odr_version(vd); return true; } void STIDecoder::packet_completed() { auto seq = m_dispatcher.get_seq_info(); m_data_collector.assemble(seq); } }