diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 88ce123..995a9f1 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -13,10 +13,7 @@ "USE_GPSD=1", "all" ], - "group": { - "kind": "build", - "isDefault": true - }, + "group": "build", "problemMatcher": [] }, { @@ -94,7 +91,10 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": [] } ] diff --git a/DStarBase/AMBEFileReader.cpp b/DStarBase/AMBEFileReader.cpp new file mode 100644 index 0000000..119ea4a --- /dev/null +++ b/DStarBase/AMBEFileReader.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2021 by Geoffrey Merck F4FXL / KC3FRA + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include "AMBEFileReader.h" +#include "DStarDefines.h" +#include "Log.h" + +const unsigned int SILENCE_LENGTH = 10U; + +CAMBEFileReader::CAMBEFileReader(const std::string& indexFile, const std::string& ambeFile) : +m_indexFile(indexFile), +m_ambeFile(ambeFile), +m_ambe(nullptr), +m_ambeLength(0U), +m_index() +{ + +} + +CAMBEFileReader::~CAMBEFileReader() +{ + if(m_ambe != nullptr) { + delete[] m_ambe; + } +} + +bool CAMBEFileReader::read() +{ + bool ret = readAmbe() && readIndex(); + return ret; +} + +bool CAMBEFileReader::readAmbe() +{ + struct stat sbuf; + if (stat(m_ambeFile.c_str(), &sbuf)) { + CLog::logWarning("File %s not readable\n", m_ambeFile.c_str()); + return false; + } + + unsigned int fsize = sbuf.st_size; + + FILE *file = fopen(m_ambeFile.c_str(), "rb"); + if (NULL == file) { + CLog::logError("Cannot open %s for reading\n", m_ambeFile.c_str()); + return false; + } + + CLog::logInfo("Reading %s\n", m_ambeFile.c_str()); + + unsigned char buffer[VOICE_FRAME_LENGTH_BYTES]; + + size_t n = fread(buffer, sizeof(unsigned char), 4, file); + if (n != 4) { + CLog::logError("Unable to read the header from %s\n", m_ambeFile.c_str()); + fclose(file); + return false; + } + + if (memcmp(buffer, "AMBE", 4)) { + CLog::logError("Invalid header from %s\n", m_ambeFile.c_str()); + fclose(file); + return false; + } + + // Length of the file minus the header + unsigned int length = fsize - 4U; + + // Hold the file data plus silence at the end + m_ambe = new unsigned char[length + SILENCE_LENGTH * VOICE_FRAME_LENGTH_BYTES]; + m_ambeLength = length / VOICE_FRAME_LENGTH_BYTES; + + // Add silence to the beginning of the buffer + unsigned char* p = m_ambe; + for (unsigned int i = 0U; i < SILENCE_LENGTH; i++, p += VOICE_FRAME_LENGTH_BYTES) + memcpy(p, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + n = fread(p, 1, length, file); + if (n != length) { + CLog::logError("Unable to read the AMBE data from %s\n", m_ambeFile.c_str()); + fclose(file); + delete[] m_ambe; + m_ambeLength = 0U; + m_ambe = nullptr; + return false; + } + + fclose(file); + + return true; +} + +bool CAMBEFileReader::readIndex() +{ + struct stat sbuf; + + if (stat(m_indexFile.c_str(), &sbuf)) { + CLog::logError("File %s not readable\n", m_indexFile.c_str()); + return false; + } + + FILE *file = fopen(m_indexFile.c_str(), "r"); + if (file == nullptr) { + CLog::logError("Cannot open %s for reading\n", m_indexFile.c_str()); + return false; + } + + // Add a silence entry at the beginning + m_index[" "] = new CIndexRecord(" ", 0, SILENCE_LENGTH); + + CLog::logInfo("Reading %s\n", m_indexFile.c_str()); + + char line[128]; + while (fgets(line, 128, file)) { + if (strlen(line) && '#'!=line[0]) { + const std::string space(" \t\r\n"); + std::string name(strtok(line, space.c_str())); + std::string strt(strtok(NULL, space.c_str())); + std::string leng(strtok(NULL, space.c_str())); + + if (name.size() && strt.size() && leng.size()) { + unsigned long start = std::stoul(strt); + unsigned long length = std::stoul(leng); + + if (start >= m_ambeLength || (start + length) >= m_ambeLength) + CLog::logInfo("The start or end for *%s* is out of range, start: %lu, end: %lu\n", name.c_str(), start, start + length); + else + m_index[name] = new CIndexRecord(name, start + SILENCE_LENGTH, length); + } + } + } + + fclose(file); + + return true; +} + +bool CAMBEFileReader::lookup(const std::string &id, std::vector& data) +{ + if(m_index.count(id) == 0U) { + CLog::logError("Cannot find the AMBE index for *%s*", id.c_str()); + return false; + } + + CIndexRecord* info = m_index[id]; + unsigned int start = info->getStart(); + unsigned int length = info->getLength(); + + for (unsigned int i = 0U; i < length; i++) { + unsigned char* dataIn = m_ambe + (start + i) * VOICE_FRAME_LENGTH_BYTES; + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, dataIn, VOICE_FRAME_LENGTH_BYTES); + + CAMBEData* dataOut = new CAMBEData; + dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES); + data.push_back(dataOut); + } + + return true; +} \ No newline at end of file diff --git a/DStarBase/AMBEFileReader.h b/DStarBase/AMBEFileReader.h new file mode 100644 index 0000000..cd66e6c --- /dev/null +++ b/DStarBase/AMBEFileReader.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 by Geoffrey Merck F4FXL / KC3FRA + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#pragma once + +#include +#include +#include + +#include "AMBEData.h" + +class CIndexRecord { +public: + CIndexRecord(const std::string& name, unsigned int start, unsigned int length) : + m_name(name), + m_start(start), + m_length(length) + { + } + + std::string getName() const + { + return m_name; + } + + unsigned int getStart() const + { + return m_start; + } + + unsigned int getLength() const + { + return m_length; + } + +private: + std::string m_name; + unsigned int m_start; + unsigned int m_length; +}; + +class CAMBEFileReader +{ +public: + CAMBEFileReader(const std::string& indexFile, const std::string& ambeFile); + ~CAMBEFileReader(); + bool read(); + bool lookup(const std::string &id, std::vector& data); + +private: + bool readAmbe(); + bool readIndex(); + + std::string m_indexFile; + std::string m_ambeFile; + unsigned char* m_ambe; + unsigned int m_ambeLength; + std::unordered_map m_index; +}; \ No newline at end of file diff --git a/Tests/AMBEFileReader/fr_FR.ambe b/Tests/AMBEFileReader/fr_FR.ambe new file mode 100644 index 0000000..c81d063 Binary files /dev/null and b/Tests/AMBEFileReader/fr_FR.ambe differ diff --git a/Tests/AMBEFileReader/fr_FR.indx b/Tests/AMBEFileReader/fr_FR.indx new file mode 100644 index 0000000..f67b54a --- /dev/null +++ b/Tests/AMBEFileReader/fr_FR.indx @@ -0,0 +1,44 @@ +0 94 29 +1 140 16 +2 173 20 +3 215 19 +4 255 24 +5 298 29 +6 344 34 +7 398 25 +8 440 24 +9 481 23 +alpha 521 29 +bravo 568 27 +charlie 615 30 +delta 666 30 +A 714 21 +B 753 27 +C 796 34 +D 848 30 +E 895 23 +F 936 27 +G 982 27 +H 1026 30 +I 1075 20 +J 1113 29 +K 1162 22 +L 1201 28 +M 1248 28 +N 1295 28 +O 1341 23 +P 1386 21 +Q 1426 23 +R 1466 28 +S 1511 31 +T 1567 21 +U 1606 22 +V 1646 30 +W 1693 46 +X 1756 31 +Y 1806 21 +Z 1844 14 +linkedto 1858 35 +notlinked 1941 46 +linkingto 2008 38 +isbusy 2091 47 diff --git a/Tests/AMBEFileReader/lookup.cpp b/Tests/AMBEFileReader/lookup.cpp new file mode 100644 index 0000000..f239eae --- /dev/null +++ b/Tests/AMBEFileReader/lookup.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2021 by Geoffrey Merck F4FXL / KC3FRA + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include "AMBEFileReader.h" +#include "AMBEData.h" + +namespace AMBEFileReaderTests +{ + class AMBEFileReader_lookup : public ::testing::Test { + + }; + + TEST_F(AMBEFileReader_lookup, nonExistentFiles) + { + CAMBEFileReader reader("/this/file/does/not/exist", "/neither/does/this/file"); + bool res = reader.read(); + EXPECT_FALSE(res) << "read shall return false on non existent files"; + + std::vector data; + res = reader.lookup("0", data); + EXPECT_FALSE(res) << "read shall return false on non existent files"; + + for(auto d : data) { + delete d; + } + } + + TEST_F(AMBEFileReader_lookup, onlyIndexFileExists) + { + std::string indexFile = std::string(std::filesystem::current_path()) + "/Tests/AMBEFileReader/fr_FR.indx"; + CAMBEFileReader reader(indexFile, "/this/file/does/not/exist"); + bool res = reader.read(); + EXPECT_FALSE(res) << "read shall return false on non existent file"; + + std::vector data; + res = reader.lookup("0", data); + EXPECT_FALSE(res) << "read shall return false on non existent file"; + + for(auto d : data) { + delete d; + } + } + + TEST_F(AMBEFileReader_lookup, onlyAmbeFileExists) + { + std::string ambeFile = std::string(std::filesystem::current_path()) + "/Tests/AMBEFileReader/fr_FR.ambe"; + CAMBEFileReader reader("/this/file/does/not/exist", ambeFile); + bool res = reader.read(); + EXPECT_FALSE(res) << "read shall return false on non existent file"; + + std::vector data; + res = reader.lookup("0", data); + EXPECT_FALSE(res) << "read shall return false on non existent file"; + + for(auto d : data) { + delete d; + } + } + + TEST_F(AMBEFileReader_lookup, validId) + { + std::string indexFile = std::string(std::filesystem::current_path()) + "/Tests/AMBEFileReader/fr_FR.indx"; + std::string ambeFile = std::string(std::filesystem::current_path()) + "/Tests/AMBEFileReader/fr_FR.ambe"; + CAMBEFileReader reader(indexFile, ambeFile); + bool res = reader.read(); + EXPECT_TRUE(res) << "read shall return true on existent files"; + + std::vector data; + res = reader.lookup("0", data); + EXPECT_TRUE(res) << "read shall return true on existent files and valid Id"; + EXPECT_NE(data.size(), 0U) << "Vector shall contain data"; + + for(auto d : data) { + delete d; + } + } + + TEST_F(AMBEFileReader_lookup, invalidId) + { + std::string indexFile = std::string(std::filesystem::current_path()) + "/Tests/AMBEFileReader/fr_FR.indx"; + std::string ambeFile = std::string(std::filesystem::current_path()) + "/Tests/AMBEFileReader/fr_FR.ambe"; + CAMBEFileReader reader(indexFile, ambeFile); + bool res = reader.read(); + EXPECT_TRUE(res) << "read shall return true on existent files"; + + std::vector data; + res = reader.lookup("This Id does not exist", data); + EXPECT_FALSE(res) << "read shall return false on existent files and invalid Id"; + EXPECT_EQ(data.size(), 0U) << "Vector shall not contain data"; + + for(auto d : data) { + delete d; + } + } +} \ No newline at end of file diff --git a/Tests/AMBEFileReader/read.cpp b/Tests/AMBEFileReader/read.cpp new file mode 100644 index 0000000..99f7bf4 --- /dev/null +++ b/Tests/AMBEFileReader/read.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 by Geoffrey Merck F4FXL / KC3FRA + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include "AMBEFileReader.h" + +namespace AMBEFileReaderTests +{ + class AMBEFileReader_read : public ::testing::Test { + + }; + + TEST_F(AMBEFileReader_read, nonExistentFiles) + { + CAMBEFileReader reader("/this/file/does/not/exist", "/neither/does/this/file"); + bool res = reader.read(); + EXPECT_FALSE(res) << "read shall return false on non existent files"; + } + + TEST_F(AMBEFileReader_read, onlyIndexFileExists) + { + std::string indexFile = std::string(std::filesystem::current_path()) + "/Tests/AMBEFileReader/fr_FR.indx"; + CAMBEFileReader reader(indexFile, "/this/file/does/not/exist"); + bool res = reader.read(); + EXPECT_FALSE(res) << "read shall return false on non existent file"; + } + + TEST_F(AMBEFileReader_read, onlyAmbeFileExists) + { + std::string ambeFile = std::string(std::filesystem::current_path()) + "/Tests/AMBEFileReader/fr_FR.ambe"; + CAMBEFileReader reader("/this/file/does/not/exist", ambeFile); + bool res = reader.read(); + EXPECT_FALSE(res) << "read shall return false on non existent file"; + } + + TEST_F(AMBEFileReader_read, bothFileExist) + { + std::string indexFile = std::string(std::filesystem::current_path()) + "/Tests/AMBEFileReader/fr_FR.indx"; + std::string ambeFile = std::string(std::filesystem::current_path()) + "/Tests/AMBEFileReader/fr_FR.ambe"; + CAMBEFileReader reader(indexFile, ambeFile); + bool res = reader.read(); + EXPECT_TRUE(res) << "read shall return true on existent files"; + } +} \ No newline at end of file