Files
op-pedal/src/RegDb.cpp
T
2022-03-02 13:59:58 -05:00

250 lines
7.5 KiB
C++

// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "pch.h"
#include "RegDb.hpp"
#include <fstream>
#include <filesystem>
#include <unordered_set>
#include <arpa/inet.h>
#include "PiPedalException.hpp"
#include <cstring>
#include <fcntl.h>
#define REGDB_MAGIC 0x52474442
#define REGDB_VERSION 19
using namespace pipedal;
using namespace std;
struct RegDbFileHeader {
uint32_t magic;
uint32_t version;
uint32_t countryOffset;
uint32_t countryCount;
uint32_t signatureLength;
void toNs() {
this->magic = htonl(this->magic);
this->version = htonl(this->version);
this->countryOffset = htonl(this->countryOffset);
this->countryCount = htonl(this->countryCount);
this->signatureLength = htonl(this->signatureLength);
}
};
struct CountryHeader {
char alpha2[2];
uint8_t pad;
uint8_t dfsRegion; // first 2 bits only.
uint32_t rulesOffset;
void toNs() {
this->rulesOffset = htonl(rulesOffset);
}
};
struct RulesCollection {
uint32_t ruleCount;
uint32_t ruleOffsets[1];
void toNs() {
this->ruleCount = htonl(this->ruleCount);
uint32_t *p = ruleOffsets;
for (uint32_t i = 0; i < this->ruleCount; ++i)
{
*p = htonl(*p);
++p;
}
}
};
struct Rule {
uint32_t frequencyRangeOffset;
uint32_t powerRuleOffset;
uint32_t flags;
void toNs() {
frequencyRangeOffset = htonl(frequencyRangeOffset);
powerRuleOffset = htonl(powerRuleOffset);
flags = htonl(flags);
}
};
struct FrequencyRange {
uint32_t startFrequency; // in khz.
uint32_t endFrequency; // in khz.
uint32_t maxBandwidth; // in khz.
void toNs() {
startFrequency = htonl(startFrequency);
endFrequency = htonl(endFrequency);
maxBandwidth = htonl(maxBandwidth);
}
};
struct PowerRule {
uint32_t maximumAntennaGain;
uint32_t maximumEirp;
void toNs() {
maximumAntennaGain = htonl(maximumAntennaGain);
maximumEirp = htonl(maximumEirp);
}
};
const char*filePaths[] =
{
"/usr/lib/crda/regulatory.bin"
};
static std::filesystem::path getFilePath()
{
for (size_t i = 0; i < sizeof(filePaths)/sizeof(filePaths[0]); ++i)
{
std::filesystem::path path(filePaths[i]);
if (std::filesystem::exists(path)) {
return path;
}
}
throw PiPedalException("Could not find 'regulatory.bin' file.");
}
RegDb::~RegDb()
{
delete[] pData;
}
RegDb::RegDb()
:pData(nullptr)
{
RegDbFileHeader header;
std::filesystem::path path = getFilePath();
ifstream f(path);
if (!f.is_open()) {
stringstream s;
s << "Can't read " << path;
throw PiPedalException(s.str());
}
size_t fileSize = std::filesystem::file_size(path);
this->pData = new uint8_t[fileSize];
f.read((char*)this->pData,fileSize);
// convert from big-endian to local byte order;
RegDbFileHeader *fileHeader = (RegDbFileHeader*)pData;
fileHeader->toNs();
if (fileHeader->magic != REGDB_MAGIC)
{
throw PiPedalException("Invalid file format.");
}
std::unordered_set<uint32_t> visited;
CountryHeader*countryHeader = (CountryHeader*)(pData + fileHeader->countryOffset);
for (uint32_t i = 0; i < fileHeader->countryCount; ++i)
{
countryHeader->toNs();
if (visited.count(countryHeader->rulesOffset) == 0)
{
visited.insert(countryHeader->rulesOffset);
RulesCollection*pRules = (RulesCollection*)(pData + countryHeader->rulesOffset);
pRules->toNs();
for (uint32_t nRule = 0; nRule < pRules->ruleCount; ++nRule)
{
uint32_t ruleOffset = pRules->ruleOffsets[nRule];
if (visited.count(ruleOffset) == 0)
{
visited.insert(ruleOffset);
Rule *pRule = (Rule*)(pData + ruleOffset);
pRule->toNs();
if (visited.count(pRule->frequencyRangeOffset) == 0)
{
visited.insert(pRule->frequencyRangeOffset);
FrequencyRange*pf = (FrequencyRange*)(pData + pRule->frequencyRangeOffset);
pf->toNs();
}
if (visited.count(pRule->powerRuleOffset) == 0)
{
visited.insert(pRule->powerRuleOffset);
PowerRule *pr = (PowerRule*)(pData + pRule->powerRuleOffset);
pr->toNs();
}
}
}
}
++countryHeader;
}
}
bool CrdbRules::GetFrequencyRule(uint32_t frequencyRuleMHz, FrequencyRule*pResult)
{
memset(pResult,0,sizeof(FrequencyRule));
RulesCollection*pRules = (RulesCollection*)(regDb->pData + countryOffset);
uint32_t frequencyRuleKHz = frequencyRuleMHz*1000;
for (uint32_t i = 0; i < pRules->ruleCount; ++i)
{
Rule *pRule = (Rule*)(regDb->pData + pRules->ruleOffsets[i]);
FrequencyRange*pf = (FrequencyRange*)(regDb->pData + pRule->frequencyRangeOffset);
if (frequencyRuleKHz >= pf->startFrequency && frequencyRuleKHz < pf->endFrequency)
{
PowerRule *pr = (PowerRule*)(regDb->pData + pRule->powerRuleOffset);
pResult->flags = (RegRuleFlags)(pRule->flags);
pResult->maximumAntennaGain = pr->maximumAntennaGain;
pResult->maximumEirp = pr->maximumEirp;
return true;
}
}
return false;
}
CrdbRules*RegDb::GetCrdbRules(const char*countryCodeIso633)
{
const char c0 = countryCodeIso633[0];
if (c0 == '\0') return nullptr;
const char c1 = countryCodeIso633[1];
RegDbFileHeader *fileHeader = (RegDbFileHeader*)pData;
CountryHeader*countryHeader = (CountryHeader*)(pData + fileHeader->countryOffset);
for (uint32_t i = 0; i < fileHeader->countryCount; ++i)
{
if (countryHeader->alpha2[0] == c0 && countryHeader->alpha2[1] == c1) {
return new CrdbRules(
this,
countryHeader->rulesOffset,
(DfsRegion)(countryHeader->dfsRegion & 0x03)
);
}
++countryHeader;
}
return nullptr;
}
CrdbRules::CrdbRules(RegDb *regDb,uint32_t countryOffset, DfsRegion dfsRegion )
: regDb(regDb),
countryOffset(countryOffset),
dfsRegion(dfsRegion)
{
}
DfsRegion CrdbRules::GetDfsRegion() const {
return this->dfsRegion;
}