Files
terminal-web-server/src/api-driver/structs.cpp

966 lines
34 KiB
C++

#include "api-driver/structs.h"
#include "api-driver/proxy.h"
#include "common/nlohmann/json.hpp"
#include <iomanip>
#include <sys/sysinfo.h>
#include <boost/property_tree/ptree.hpp>
#include "terminal_api_driver.h"
#define TIME_NOW() std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count()
static inline void rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch);
}).base(), s.end());
}
static inline const char* boolAsStr(bool value) {
return value ? "true" : "false";
}
std::ostream& operator<<(std::ostream& out, CP_Result result) {
switch (result) {
case OK: out << "OK"; break;
case TIMEOUT: out << "TIMEOUT"; break;
case ERROR: out << "ERROR"; break;
case ABORT: out << "ABORT"; break;
case BUSY: out << "BUSY"; break;
default:
out << static_cast<int>(result);
}
return out;
}
std::string makeTimepointFromMillis(int64_t unix_time_ms) {
// Преобразуем миллисекунды в микросекунды для std::chrono
auto time_point = std::chrono::time_point<std::chrono::system_clock,
std::chrono::microseconds>(std::chrono::microseconds(unix_time_ms * 1000));
auto tp = std::chrono::system_clock::to_time_t(time_point);
tm* t = std::localtime(&tp);
std::stringstream ss;
ss << std::put_time(t, "%Y-%m-%d %H:%M:%S");
auto ms = (unix_time_ms % 1000);
ss << '.' << std::setw(3) << std::setfill('0') << ms;
return ss.str();
}
#ifdef API_OBJECT_DEBUG_METRICS_ENABLE
api_driver::obj::StatisticsLogger::StatisticsLogger(): timeStart(TIME_NOW()) {}
nlohmann::json api_driver::obj::StatisticsLogger::getSettings() {
std::lock_guard _lock(mutex);
nlohmann::json res;
res["en"] = this->logEn;
res["logPeriodMs"] = logPeriodMs.load();
res["maxAgeMs"] = maxAgeMs.load();
return res;
}
void api_driver::obj::StatisticsLogger::setSettings(const nlohmann::json& data) {
const bool newEn = data.value("en", logEn);
const int newInterval = data.value("logPeriodMs", logPeriodMs.load());
const int newMaxAgeMs = data.value("maxAgeMs", maxAgeMs.load());
std::lock_guard _lock(this->mutex);
this->logPeriodMs = newInterval;
this->maxAgeMs = newMaxAgeMs;
if (newEn != this->logEn) {
if (newEn) {
this->logFile.open(LOG_FILENAME, std::ios::out);
if (this->logFile.is_open()) {
const auto* header = "timestamp\tcnt ok\tcnt bad\tfine freq dem\tcrs freq dem\tcrs freq compensator\tcrs time est\tfine time est\tmax level corr\tcurrent delay\tSNR\tcurrent modcod\tfine freq compensator\tind freq grb\tind freq tochn\tind filt adapt\tfilter corr cinc\tcorr cnt\tRSS\tcor erl\tcor lat\tgc gain\tpower pl rx\n";
this->logFile.write(header, static_cast<std::streamsize>(strlen(header)));
this->logEn = true;
this->timeStart = TIME_NOW();
}
} else {
if (this->logFile.is_open()) {
this->logFile.close();
}
this->logEn = false;
}
}
}
void api_driver::obj::StatisticsLogger::updateCallback(proxy::CpProxy &cp) {
if (!logEn) return;
debug_metrics dm{};
cp.getDebugMetrics(dm);
putItem(dm);
}
void api_driver::obj::StatisticsLogger::putItem(const debug_metrics &item) {
std::lock_guard _lock(this->mutex);
if (!logEn) return;
if (this->logFile.is_open()) {
std::stringstream res;
res << makeTimepointFromMillis(TIME_NOW()) << '\t';
res << item.cnt_ok << '\t';
res << item.cnt_bad << '\t';
res << item.fine_freq_dem << '\t';
res << item.crs_freq_dem << '\t';
res << item.crs_freq_compensator << '\t';
res << item.crs_time_est << '\t';
res << item.fine_time_est << '\t';
res << item.max_level_corr << '\t';
res << item.current_delay << '\t';
res << item.SNR << '\t';
res << item.current_modcod << '\t';
res << item.fine_freq_compensator << '\t';
res << item.ind_freq_grb << '\t';
res << item.ind_freq_tochn << '\t';
res << item.ind_filt_adapt << '\t';
res << item.filter_corr_cinc << '\t';
res << item.corr_cnt << '\t';
res << item.RSS << '\t';
res << item.cor_erl << '\t';
res << item.cor_lat << '\t';
res << item.gc_gain << '\t';
res << item.power_pl_rx << '\n';
const auto out = res.str();
this->logFile.write(out.c_str(), static_cast<std::streamsize>(out.length()));
this->logFile.flush();
}
}
api_driver::obj::StatisticsLogger::~StatisticsLogger() = default;
#endif
#ifdef API_OBJECT_NETWORK_SETTINGS_ENABLE
static int calculateSubnetMask(const std::string& subnet_mask) {
int mask = 0;
std::istringstream iss(subnet_mask);
std::string octet;
while (std::getline(iss, octet, '.')) {
int octet_value = std::stoi(octet);
for (int i = 7; i >= 0; i--) {
if (octet_value & (1 << i)) {
mask++;
}
}
}
return mask;
}
/**
* Преобразует строку вида `1.2.3.4/24` в пару строк вида `1.2.3.4` `255.255.255.0`
*/
std::pair<std::string, std::string> splitIpAndMask(const std::string& input) {
auto pos = input.find('/');
if (pos == std::string::npos) {
// Обработка ошибки: нет символа '/'
throw std::runtime_error("address not contains mask");
}
std::string ip = input.substr(0, pos);
const unsigned int mask_int = std::stoul(input.substr(pos + 1));
if (mask_int > 32) {
throw std::runtime_error("invalid mask");
}
std::string mask_binary = std::string(mask_int, '1') + std::string(32 - mask_int, '0');
std::string mask_str;
for (unsigned int i = 0; i < 4; ++i) {
std::string octet = mask_binary.substr(i * 8u, 8);
int octet_value = std::stoi(octet, nullptr, 2);
mask_str += std::to_string(octet_value) + (i < 3 ? "." : "");
}
return std::make_pair(ip, mask_str);
}
api_driver::obj::TerminalNetworkSettings::TerminalNetworkSettings() { loadDefaults(); }
api_driver::obj::TerminalNetworkSettings::TerminalNetworkSettings(const TerminalNetworkSettings &src) = default;
api_driver::obj::TerminalNetworkSettings & api_driver::obj::TerminalNetworkSettings::operator=(const TerminalNetworkSettings &src) = default;
void api_driver::obj::TerminalNetworkSettings::loadDefaults() {
managementIp = "0.0.0.0/24";
managementGateway = "";
isL2 = true;
dataIp = "0.0.0.0";
dataMtu = 1500;
serverName = DEFAULT_SERVER_NAME;
}
void api_driver::obj::TerminalNetworkSettings::updateCallback(proxy::CpProxy &cp) {
loadDefaults();
try {
managementIp = cp.getNetwork("addr");
managementIp += "/";
managementIp += std::to_string(calculateSubnetMask(cp.getNetwork("mask")));
managementGateway = cp.getNetwork("gateway");
if (cp.getNetwork("mode") == "tun") {
isL2 = false;
dataIp = cp.getNetwork("addr_data");
} else {
isL2 = true;
}
dataMtu = 1500;
serverName = cp.getNetwork("name_serv");
if (serverName.empty()) {
serverName = DEFAULT_SERVER_NAME;
}
} catch (std::exception& e) {
throw std::runtime_error(std::string("api_driver::obj::TerminalNetworkSettings::updateCallback() error: ") + e.what());
}
}
void api_driver::obj::TerminalNetworkSettings::updateFromJson(const nlohmann::json &data) {
managementIp = data.value("managementIp", managementIp);
isL2 = data.value("isL2", isL2);
dataIp = data.value("dataIp", dataIp);
dataMtu = data.value("dataMtu", dataMtu);
serverName = data.value("serverName", serverName);
}
void api_driver::obj::TerminalNetworkSettings::store(proxy::CpProxy& cp) {
try {
cp.setNetwork("mode", isL2 ? "tap" : "tun");
auto [mAddr, mMask] = splitIpAndMask(managementIp);
cp.setNetwork("addr", mAddr);
cp.setNetwork("mask", mMask);
if (!isL2) {
cp.setNetwork("addr_data", dataIp);
}
cp.setNetwork("gateway", managementGateway);
// cp.setNetwork("data_mtu", std::to_string(dataMtu));
cp.setNetwork("name_serv", serverName);
} catch (std::exception& e) {
throw std::runtime_error(std::string("api_driver::obj::TerminalNetworkSettings::store() error: ") + e.what());
}
}
nlohmann::json api_driver::obj::TerminalNetworkSettings::asJson() {
nlohmann::json res;
res["isL2"] = isL2;
res["managementIp"] = managementIp;
res["managementGateway"] = managementGateway;
res["dataIp"] = dataIp;
res["dataMtu"] = dataMtu;
res["serverName"] = serverName;
return res;
}
api_driver::obj::TerminalNetworkSettings::~TerminalNetworkSettings() = default;
#endif
#ifdef API_OBJECT_QOS_SETTINGS_ENABLE
api_driver::obj::TerminalQosSettings::TerminalQosSettings(): qosSettingsJson(DEFAULT_QOS_CLASSES) {};
api_driver::obj::TerminalQosSettings::TerminalQosSettings(const TerminalQosSettings &src) = default;
api_driver::obj::TerminalQosSettings & api_driver::obj::TerminalQosSettings::operator=(const TerminalQosSettings &src) = default;
void api_driver::obj::TerminalQosSettings::loadDefaults() {
qosEnabled = false;
qosSettingsJson = DEFAULT_QOS_CLASSES;
}
void api_driver::obj::TerminalQosSettings::updateCallback(proxy::CpProxy &cp) {
auto [profile, en] = cp.getQosSettings();
qosEnabled = en;
try {
qosSettingsJson = nlohmann::json::parse(profile);
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(warning) << "api_driver::obj::TerminalQosSettings::updateCallback(): Failed to parse QoS settings json: " << e.what();
qosSettingsJson = DEFAULT_QOS_CLASSES;
}
}
void api_driver::obj::TerminalQosSettings::updateFromJson(const nlohmann::json &data) {
qosEnabled = data.value("en", qosEnabled);
qosSettingsJson = data.value("profile", qosSettingsJson);
}
void api_driver::obj::TerminalQosSettings::store(proxy::CpProxy &cp) {
cp.setQosSettings(qosSettingsJson.dump(), qosEnabled);
}
nlohmann::json api_driver::obj::TerminalQosSettings::asJson() {
nlohmann::json res;
res["en"] = qosEnabled;
res["profile"] = qosSettingsJson;
return res;
}
api_driver::obj::TerminalQosSettings::~TerminalQosSettings() = default;
#endif
api_driver::obj::TerminalFirmwareVersion::TerminalFirmwareVersion() = default;
api_driver::obj::TerminalFirmwareVersion::TerminalFirmwareVersion(const TerminalFirmwareVersion &src) = default;
api_driver::obj::TerminalFirmwareVersion & api_driver::obj::TerminalFirmwareVersion::operator=(const TerminalFirmwareVersion &src) = default;
void api_driver::obj::TerminalFirmwareVersion::load(proxy::CpProxy &cp) {
version = cp.getNetwork("version");
modemId = cp.getNetwork("chip_id");
rtrim(modemId);
modemSn = cp.getNetwork("serial");
macMang = cp.getNetwork("mac_eth0");
macData = cp.getNetwork("mac_eth1");
}
nlohmann::json api_driver::obj::TerminalFirmwareVersion::asJson() {
nlohmann::json res;
res["version"] = version;
res["modemId"] = modemId;
res["modemSn"] = modemSn;
res["macMang"] = macMang;
res["macData"] = macData;
return res;
}
api_driver::obj::TerminalFirmwareVersion::~TerminalFirmwareVersion() = default;
api_driver::obj::TerminalState::TerminalState() = default;
void api_driver::obj::TerminalState::updateCallback(proxy::CpProxy& cp) {
modulator_state mod{};
modulator_settings modSet{};
demodulator_state demod{};
#ifdef MODEM_IS_SCPC
CinC_state cinc{};
#endif
try {
cp.getModState(mod);
cp.getDemodState(demod);
cp.getModSettings(modSet);
fTxState = modSet.tx_is_on;
fIsTest = modSet.tx_is_on && (!modSet.is_carrier || modSet.is_test_data);
#ifdef MODEM_IS_SCPC
fIsCinC = modSet.is_cinc;
if (fIsCinC) {
cp.getCincState(cinc);
}
#endif
#ifdef MODEM_IS_TDMA
fInitState = cp.getDmaDebug("status_init");
#endif
} catch (std::exception& e) {
throw std::runtime_error(std::string("api_driver::obj::TerminalState::updateCallback() error: ") + e.what());
}
fRxState = demod.locks.sym_sync_lock && demod.locks.freq_lock && demod.locks.afc_lock && demod.locks.pkt_sync;
#ifndef MODEM_IS_SHPS
fRxSymSyncLock = demod.locks.sym_sync_lock;
fRxFreqSearchLock = demod.locks.freq_lock;
fRxAfcLock = demod.locks.afc_lock;
fRxPktSync = demod.locks.pkt_sync;
#endif
fRxSnr = demod.snr;
fRxRssi = demod.rssi;
#ifndef MODEM_IS_SHPS
fRxModcod = demod.modcod;
fRxFrameSizeNormal = !demod.is_short;
fRxIsPilots = demod.is_pilots;
fRxSymError = demod.sym_err;
fRxFreqErr = demod.crs_freq_err;
#endif
fRxFreqErrAcc = demod.fine_freq_err;
fRxInputSignalLevel = demod.if_overload;
fRxPllError = demod.afc_err;
fRxSpeedOnRxKbit = static_cast<double>(demod.speed_in_bytes_rx) / 128.0;
fRxSpeedOnIifKbit = static_cast<double>(demod.speed_in_bytes_rx_iface) / 128.0;
fRxPacketsOk = demod.packet_ok_cnt;
fRxPacketsBad = demod.packet_bad_cnt;
#ifndef MODEM_IS_SHPS
fRxPacketsDummy = demod.dummy_cnt;
fTxModcod = mod.modcod;
#endif
fTxSpeedOnTxKbit = static_cast<double>(mod.speed_in_bytes_tx) / 128.0;
fTxSpeedOnIifKbit = static_cast<double>(mod.speed_in_bytes_tx_iface) / 128.0;
#ifdef MODEM_IS_SCPC
fTxSnr = mod.snr_remote;
fTxFrameSizeNormal = !mod.is_short;
fTxIsPilots = mod.is_pilots;
fCincOcc = cinc.ratio_signal_signal;
fCincCorrelator = cinc.carrier_lock;
fCincCorrelatorFails = cinc.cnt_bad_lock;
fCincFreqErr = cinc.freq_error_offset;
fCincFreqErrAcc = cinc.freq_fine_estimate;
fCincChannelDelay = cinc.delay_dpdi;
#endif
fTxCenterFreq = modSet.central_freq_in_kGz;
fTxSymSpeed = static_cast<double>(modSet.baudrate) / 1000.0;
}
nlohmann::json api_driver::obj::TerminalState::asJson() {
nlohmann::json res{};
res["initState"] = fInitState;
res["testState"] = fIsTest;
#ifdef MODEM_IS_SCPC
res["isCinC"] = fIsCinC;
#endif
res["rx"]["state"] = fRxState;
#ifndef MODEM_IS_SHPS
res["rx"]["sym_sync_lock"] = fRxSymSyncLock;
res["rx"]["freq_search_lock"] = fRxFreqSearchLock;
res["rx"]["afc_lock"] = fRxAfcLock;
res["rx"]["pkt_sync"] = fRxPktSync;
#endif
res["rx"]["snr"] = fRxSnr;
res["rx"]["rssi"] = fRxRssi;
#ifndef MODEM_IS_SHPS
res["rx"]["modcod"] = fRxModcod;
res["rx"]["frameSizeNormal"] = fRxFrameSizeNormal;
res["rx"]["isPilots"] = fRxIsPilots;
res["rx"]["symError"] = fRxSymError;
res["rx"]["freqErr"] = fRxFreqErr;
#endif
res["rx"]["freqErrAcc"] = fRxFreqErrAcc;
res["rx"]["inputSignalLevel"] = fRxInputSignalLevel;
res["rx"]["pllError"] = fRxPllError;
res["rx"]["speedOnRxKbit"] = fRxSpeedOnRxKbit;
res["rx"]["speedOnIifKbit"] = fRxSpeedOnIifKbit;
res["rx"]["packetsOk"] = fRxPacketsOk;
res["rx"]["packetsBad"] = fRxPacketsBad;
#ifndef MODEM_IS_SHPS
res["rx"]["packetsDummy"] = fRxPacketsDummy;
res["tx"]["modcod"] = fTxModcod;
#endif
res["tx"]["state"] = fTxState;
res["tx"]["speedOnTxKbit"] = fTxSpeedOnTxKbit;
res["tx"]["speedOnIifKbit"] = fTxSpeedOnIifKbit;
#ifdef MODEM_IS_SCPC
res["tx"]["snr"] = fTxSnr;
res["tx"]["frameSizeNormal"] = fTxFrameSizeNormal;
res["tx"]["isPilots"] = fTxIsPilots;
if (fIsCinC) {
if (fTxState) {
res["cinc"]["correlator"] = fCincCorrelator;
} else {
res["cinc"]["correlator"] = nullptr;
}
res["cinc"]["occ"] = fCincOcc;
res["cinc"]["correlatorFails"] = fCincCorrelatorFails;
res["cinc"]["freqErr"] = fCincFreqErr;
res["cinc"]["freqErrAcc"] = fCincFreqErrAcc;
res["cinc"]["channelDelay"] = fCincChannelDelay;
} else {
res["cinc"]["correlator"] = nullptr;
}
#endif
res["tx"]["centerFreq"] = fTxCenterFreq;
res["tx"]["symSpeed"] = fTxSymSpeed;
return res;
}
api_driver::obj::TerminalState::~TerminalState() = default;
api_driver::obj::TerminalDeviceState::TerminalDeviceState() = default;
api_driver::obj::TerminalDeviceState::TerminalDeviceState(const TerminalDeviceState &src) = default;
api_driver::obj::TerminalDeviceState & api_driver::obj::TerminalDeviceState::operator=(const TerminalDeviceState &src) = default;
void api_driver::obj::TerminalDeviceState::updateCallback(proxy::CpProxy &cp) {
{
device_state ds{};
cp.getDeviceState(ds);
fTempAdrv = ds.adrv_temp;
fTempZynq = ds.pl_temp;
fTempFpga = ds.zynq_temp;
}
#ifdef MODEM_IS_TDMA
{
progress_msg ds{};
cp.getUpdateStatus(ds);
fUpgradeStatus = ds.status;
fUpgradePercent = ds.dwl_percent;
fUpgradeImage = ds.cur_image;
}
#endif
struct sysinfo info{};
sysinfo(&info);
const double f_load = 100.0 / ((1 << SI_LOAD_SHIFT) * get_nprocs());
fOsUptime = info.uptime;
fOsLoad1 = f_load * static_cast<double>(info.loads[0]);
fOsLoad5 = f_load * static_cast<double>(info.loads[1]);
fOsLoad15 = f_load * static_cast<double>(info.loads[2]);
fOsTotalram = (info.totalram * info.mem_unit) >> 20; // Mb
fOsFreeram = (info.totalram * info.mem_unit) >> 20; // Mb
fOsProcs = info.procs;
}
nlohmann::json api_driver::obj::TerminalDeviceState::asJson() const {
nlohmann::json res;
res["uptime"] = fOsUptime;
res["load1min"] = fOsLoad1;
res["load5min"] = fOsLoad5;
res["load15min"] = fOsLoad15;
res["totalram"] = fOsTotalram;
res["freeram"] = fOsFreeram;
res["procs"] = fOsProcs;
res["adrv"] = fTempAdrv;
res["fpga"] = fTempFpga;
res["zynq"] = fTempZynq;
#ifdef MODEM_IS_TDMA
if (fUpgradeImage.empty()) {
res["upgradeStatus"] = "Нет обновлений";
res["upgradePercent"] = 0;
res["upgradeImage"] = "";
} else {
switch (fUpgradeStatus) {
case NORM_RX_OBJECT_NEW_API: res["upgradeStatus"] = "Начало загрузки"; break;
case NORM_RX_OBJECT_INFO_API: res["upgradeStatus"] = "Получено имя образа"; break;
case NORM_RX_OBJECT_UPDATED_API: res["upgradeStatus"] = "Загружается"; break;
case NORM_RX_OBJECT_COMPLETED_API: res["upgradeStatus"] = "Загрузка завершена"; break;
case NORM_RX_OBJECT_ABORTED_API: res["upgradeStatus"] = "Загрузка прервана"; break;
default: res["upgradeStatus"] = "?";
}
res["upgradePercent"] = fUpgradePercent;
res["upgradeImage"] = fUpgradeImage;
}
#endif
return res;
}
api_driver::obj::TerminalDeviceState::~TerminalDeviceState() = default;
api_driver::obj::TerminalRxTxSettings::TerminalRxTxSettings() = default;
api_driver::obj::TerminalRxTxSettings::TerminalRxTxSettings(const TerminalRxTxSettings &src) = default;
api_driver::obj::TerminalRxTxSettings & api_driver::obj::TerminalRxTxSettings::operator=(const TerminalRxTxSettings &src) = default;
void api_driver::obj::TerminalRxTxSettings::updateCallback(proxy::CpProxy &cp) {
cp.getModSettings(mod);
cp.getDemodSettings(dem);
#ifdef API_STRUCT_ACM_ENABLE
cp.getAcmSettings(acm);
#endif
#ifdef API_OBJECT_DPDI_SETTINGS_ENABLE
cp.getDpdiSettings(dpdi);
#endif
#ifdef API_OBJECT_BUCLNB_SETTINGS_ENABLE
cp.getBuclnbSettings(buclnb);
#endif
}
void api_driver::obj::TerminalRxTxSettings::storeMainSettings(proxy::CpProxy &cp) {
cp.setModSettings(mod);
cp.setDemodSettings(dem);
#ifdef API_STRUCT_ACM_ENABLE
cp.setAcmSettings(acm);
#endif
}
struct ModcodDef_t {const char* modulation; const char* speed;};
const static ModcodDef_t ModcodDefs[] = {
{.modulation = "dummy", .speed = "0"},
{.modulation = "qpsk", .speed = "1/4"},
{.modulation = "qpsk", .speed = "1/3"},
{.modulation = "qpsk", .speed = "2/5"},
{.modulation = "qpsk", .speed = "1/2"},
{.modulation = "qpsk", .speed = "3/5"},
{.modulation = "qpsk", .speed = "2/3"},
{.modulation = "qpsk", .speed = "3/4"},
{.modulation = "qpsk", .speed = "4/5"},
{.modulation = "qpsk", .speed = "5/6"},
{.modulation = "qpsk", .speed = "8/9"},
{.modulation = "qpsk", .speed = "9/10"},
{.modulation = "8psk", .speed = "3/5"},
{.modulation = "8psk", .speed = "2/3"},
{.modulation = "8psk", .speed = "3/4"},
{.modulation = "8psk", .speed = "5/6"},
{.modulation = "8psk", .speed = "8/9"},
{.modulation = "8psk", .speed = "9/10"},
{.modulation = "16apsk", .speed = "2/3"},
{.modulation = "16apsk", .speed = "3/4"},
{.modulation = "16apsk", .speed = "4/5"},
{.modulation = "16apsk", .speed = "5/6"},
{.modulation = "16apsk", .speed = "8/9"},
{.modulation = "16apsk", .speed = "9/10"},
{.modulation = "32apsk", .speed = "3/4"},
{.modulation = "32apsk", .speed = "4/5"},
{.modulation = "32apsk", .speed = "5/6"},
{.modulation = "32apsk", .speed = "8/9"},
{.modulation = "32apsk", .speed = "9/10"},
};
static const char* extractModcodModulation(uint32_t modcod, bool defaultQpsk1_4 = true) {
modcod >>= 2;
const auto* d = defaultQpsk1_4 ? ModcodDefs : ModcodDefs + 1;
if (modcod < (sizeof(ModcodDefs) / sizeof(ModcodDef_t))) {
d = ModcodDefs + modcod;
}
return d->modulation;
}
static const char* extractModcodSpeed(uint32_t modcod, bool defaultQpsk1_4 = true) {
modcod >>= 2;
const auto* d = defaultQpsk1_4 ? ModcodDefs : ModcodDefs + 1;
if (modcod < (sizeof(ModcodDefs) / sizeof(ModcodDef_t))) {
d = ModcodDefs + modcod;
}
return d->speed;
}
static bool extractModcodFrameSizeNormal(uint32_t modcod) { return (modcod & 2) == 0; }
static bool extractModcodIsPilots(uint32_t modcod) { return (modcod & 1) != 0; }
static uint32_t buildModcodFromJson(const nlohmann::json& data, uint32_t modcod, const std::string& name, bool isNormalFrame, bool isPilots = false) {
const std::string mod = data.value(name + "Modulation", extractModcodModulation(modcod));
const std::string speed = data.value(name + "Speed", extractModcodSpeed(modcod));
uint32_t _index = 0;
for (const auto& m: ModcodDefs) {
if (mod == m.modulation) {
if (modcod == 0) modcod = _index;
if (speed == m.speed) {
modcod = _index;
break;
}
}
_index++;
}
return (modcod << 2)| (isNormalFrame ? 0 : 2) | (isPilots ? 1 : 0);
}
void api_driver::obj::TerminalRxTxSettings::updateMainSettings(const nlohmann::json &data) {
// для модулятора
#ifdef MODEM_IS_SCPC
mod.is_cinc = data.value("isCinC", mod.is_cinc);
#endif
mod.tx_is_on = data.value("txEn", mod.tx_is_on);
#if defined(MODEM_IS_SCPC) || defined (MODEM_IS_SHPS)
mod.is_save_current_state = data.value("txAutoStart", mod.is_save_current_state);
mod.is_test_data = data.value("txIsTestInput", mod.is_test_data);
#endif
mod.is_carrier = !data.value("txModulatorIsTest", !mod.is_carrier);
mod.central_freq_in_kGz = data.value("txCentralFreq", mod.central_freq_in_kGz);
#if defined(MODEM_IS_SCPC) || defined (MODEM_IS_SHPS)
mod.baudrate = data.value("txBaudrate", mod.baudrate);
mod.rollof = data.value("txRolloff", mod.rollof);
mod.gold_seq_is_active = data.value("txGoldan", mod.gold_seq_is_active ? 1 : 0);
#endif
mod.attenuation = data.value("txAttenuation", mod.attenuation);
#if defined(MODEM_IS_SCPC) || defined(MODEM_IS_SHPS)
bool acmIsFrameSizeNormal = extractModcodFrameSizeNormal(mod.modcod_tx);
bool acmIsPilots = extractModcodIsPilots(mod.modcod_tx);
acmIsFrameSizeNormal = data.value("txFrameSizeNormal", acmIsFrameSizeNormal);
acmIsPilots = data.value("txIsPilots", acmIsPilots);
mod.modcod_tx = buildModcodFromJson(data, mod.modcod_tx, "dvbCcm", acmIsFrameSizeNormal, acmIsPilots);
#endif
#ifdef MODEM_IS_SHPS
mod.koef_spread = data.value("txSpreadCoef", mod.koef_spread);
mod.txFieldsDataPreamble = data.value("txFieldsDataPreamble", mod.txFieldsDataPreamble);
#endif
// демодулятор
dem.is_aru_on = data.value("rxAgcEn", dem.is_aru_on);
dem.gain = data.value("rxManualGain", dem.gain);
dem.is_rvt_iq = data.value("rxSpectrumInversion", dem.is_rvt_iq);
dem.central_freq_in_kGz = data.value("rxCentralFreq", dem.central_freq_in_kGz);
dem.baudrate = data.value("rxBaudrate", dem.baudrate);
dem.rollof = data.value("rxRolloff", dem.rollof);
#if defined(MODEM_IS_SCPC) || defined (MODEM_IS_SHPS)
dem.gold_seq_is_active = data.value("rxGoldan", dem.gold_seq_is_active ? 1 : 0);
#endif
#ifdef MODEM_IS_SHPS
dem.koef_spread = data.value("rxSpreadCoef", dem.koef_spread);
dem.rxFftShift = data.value("rxFftShift", dem.rxFftShift);
#endif
#ifdef API_STRUCT_ACM_ENABLE
// ACM
#ifdef MODEM_IS_SCPC
// эти настройки только в SCPC
acm.period_pack_acm = data.value("dvbServicePacketPeriod", acm.period_pack_acm);
acm.enable_acm = data.value("dvbIsAcm", acm.enable_acm);
acm.min_modcod_acm = buildModcodFromJson(data, acm.min_modcod_acm, "dvbAcmMin", acmIsFrameSizeNormal, acmIsPilots);
acm.max_modcod_acm = buildModcodFromJson(data, acm.max_modcod_acm, "dvbAcmMax", acmIsFrameSizeNormal, acmIsPilots);
acm.snr_threashold_acm = data.value("dvbSnrReserve", acm.snr_threashold_acm); // запас ОСШ
#endif
acm.enable_aupc = data.value("aupcEn", acm.enable_aupc);
acm.min_attenuation_aupc = data.value("aupcMinAttenuation", acm.min_attenuation_aupc);
acm.max_attenuation_aupc = data.value("aupcMaxAttenuation", acm.max_attenuation_aupc);
acm.snr_threashold_aupc = data.value("aupcRequiredSnr", acm.snr_threashold_aupc);
#endif
}
#ifdef API_OBJECT_DPDI_SETTINGS_ENABLE
static double translateCoordinates(uint8_t deg, uint8_t min) {
return static_cast<double>(deg) + static_cast<double>(min) / 60;
}
static std::tuple<uint8_t, uint8_t> translateCoordinates(double abs) {
auto deg = static_cast<uint8_t>(abs);
double min_double = (abs - deg) * 60;
auto min = static_cast<uint8_t>(min_double);
return std::make_tuple(deg, min);
}
void api_driver::obj::TerminalRxTxSettings::updateDpdiSettings(const nlohmann::json &data) {
dpdi.is_delay_window = !data.value("isPositional", !dpdi.is_delay_window);
#ifdef MODEM_IS_SCPC
dpdi.freq_offset = data.value("searchBandwidth", dpdi.freq_offset);
#endif
if (data.contains("positionStationLatitude")) {
const double pos = data["positionStationLatitude"];
const auto [g, m] = translateCoordinates(pos);
dpdi.latitude_station_grad = g;
dpdi.latitude_station_minute = m;
}
if (data.contains("positionStationLongitude")) {
const double pos = data["positionStationLongitude"];
const auto [g, m] = translateCoordinates(pos);
dpdi.longitude_station_grad = g;
dpdi.longitude_station_minute = m;
}
if (data.contains("positionSatelliteLongitude")) {
const double pos = data["positionSatelliteLongitude"];
const auto [g, m] = translateCoordinates(pos);
dpdi.longitude_sattelite_grad = g;
dpdi.longitude_sattelite_minute = m;
}
#ifdef MODEM_IS_SCPC
dpdi.min_delay = data.value("delayMin", dpdi.min_delay);
dpdi.max_delay = data.value("delayMax", dpdi.max_delay);
#else
dpdi.min_delay = 0;
dpdi.max_delay = data.value("delay", dpdi.max_delay);
#endif
}
void api_driver::obj::TerminalRxTxSettings::storeDpdiSettings(proxy::CpProxy &cp) {
cp.setDpdiSettings(dpdi);
}
#endif
#ifdef API_OBJECT_BUCLNB_SETTINGS_ENABLE
void api_driver::obj::TerminalRxTxSettings::updateBuclnbSettings(const nlohmann::json &data) {
{
// напряжение buc
int oldVoltage = 0;
if (buclnb.buc == voltage_buc::_24V) { oldVoltage = 24; }
#ifdef MODEM_IS_SCPC
if (buclnb.buc == voltage_buc::_24V) { oldVoltage = 24; }
#endif
auto result = data.value("bucPowering", oldVoltage);
switch (result) {
case 24: buclnb.buc = voltage_buc::_24V; break;
#ifdef MODEM_IS_SCPC
case 48: buclnb.buc = voltage_buc::_48V; break;
#endif
case 0:
default:
buclnb.buc = voltage_buc::DISABLE;
}
}
buclnb.is_ref_10MHz_buc = data.value("bucRefClk10M", buclnb.is_ref_10MHz_buc);
#ifdef MODEM_IS_TDMA
buclnb.lo_buc_inkHz = data.value("bucLoKhz", buclnb.lo_buc_inkHz);
#endif
{
// напряжение lnb
int oldVoltage;
switch (buclnb.lnb) {
case voltage_lnb::_13V: oldVoltage = 13; break;
case voltage_lnb::_18V: oldVoltage = 18; break;
case voltage_lnb::_24V: oldVoltage = 24; break;
default: oldVoltage = 0;
}
auto result = data.value("lnbPowering", oldVoltage);
switch (result) {
case 13: buclnb.lnb = voltage_lnb::_13V; break;
case 18: buclnb.lnb = voltage_lnb::_18V; break;
case 24: buclnb.lnb = voltage_lnb::_24V; break;
default: buclnb.lnb = voltage_lnb::DISABLE;
}
}
buclnb.is_ref_10MHz_lnb = data.value("lnbRefClk10M", buclnb.is_ref_10MHz_lnb);
#ifdef MODEM_IS_TDMA
buclnb.lo_lnb_inkHz = data.value("lnbLoKhz", buclnb.lo_lnb_inkHz);
#endif
buclnb.is_ref_10MHz_output = data.value("srvRefClk10M", buclnb.is_ref_10MHz_output);
buclnb.is_save_current_state = data.value("bucLnbAutoStart", buclnb.is_save_current_state);
}
void api_driver::obj::TerminalRxTxSettings::storeBuclnbSettings(proxy::CpProxy &cp) {
cp.setBuclnbSettings(buclnb);
}
#endif
void api_driver::obj::TerminalRxTxSettings::storeAll(proxy::CpProxy &cp) {
storeMainSettings(cp);
#ifdef API_OBJECT_DPDI_SETTINGS_ENABLE
storeDpdiSettings(cp);
#endif
#ifdef API_OBJECT_BUCLNB_SETTINGS_ENABLE
storeBuclnbSettings(cp);
#endif
}
nlohmann::json api_driver::obj::TerminalRxTxSettings::asJson() const {
nlohmann::json res;
// RX TX
{
auto& rxtx = res["rxtx"];
// для модулятора
#ifdef MODEM_IS_SCPC
rxtx["isCinC"] = mod.is_cinc;
#endif
rxtx["txEn"] = mod.tx_is_on;
#if defined(MODEM_IS_SCPC) || defined (MODEM_IS_SHPS)
rxtx["txAutoStart"] = mod.is_save_current_state;
rxtx["txIsTestInput"] = mod.is_test_data;
#endif
rxtx["txModulatorIsTest"] = !mod.is_carrier;
rxtx["txCentralFreq"] = mod.central_freq_in_kGz;
#if defined(MODEM_IS_SCPC) || defined (MODEM_IS_SHPS)
rxtx["txBaudrate"] = mod.baudrate;
rxtx["txRolloff"] = mod.rollof;
rxtx["txGoldan"] = mod.gold_seq_is_active ? 1 : 0;
#endif
rxtx["txAttenuation"] = mod.attenuation;
#ifdef MODEM_IS_SHPS
rxtx["txSpreadCoef"] = mod.koef_spread;
rxtx["txFieldsDataPreamble"] = mod.txFieldsDataPreamble;
#endif
#if defined(MODEM_IS_SCPC) || defined(MODEM_IS_SHPS)
const bool acmIsFrameSizeNormal = extractModcodFrameSizeNormal(mod.modcod_tx);
const bool acmIsPilots = extractModcodIsPilots(mod.modcod_tx);
rxtx["txFrameSizeNormal"] = acmIsFrameSizeNormal;
rxtx["txIsPilots"] = acmIsPilots;
rxtx["dvbCcmModulation"] = extractModcodModulation(mod.modcod_tx);
rxtx["dvbCcmSpeed"] = extractModcodSpeed(mod.modcod_tx);
#endif
// демодулятор
rxtx["rxAgcEn"] = dem.is_aru_on;
rxtx["rxManualGain"] = dem.gain;
rxtx["rxSpectrumInversion"] = dem.is_rvt_iq;
rxtx["rxCentralFreq"] = dem.central_freq_in_kGz;
rxtx["rxBaudrate"] = dem.baudrate;
rxtx["rxRolloff"] = dem.rollof;
#if defined(MODEM_IS_SCPC) || defined (MODEM_IS_SHPS)
rxtx["rxGoldan"] = dem.gold_seq_is_active ? 1 : 0;
#endif
#ifdef MODEM_IS_SHPS
rxtx["rxSpreadCoef"] = dem.koef_spread;
rxtx["rxFftShift"] = dem.rxFftShift;
#endif
#ifdef API_STRUCT_ACM_ENABLE
// ACM
#ifdef MODEM_IS_SCPC
// эти настройки только в SCPC
rxtx["dvbServicePacketPeriod"] = acm.period_pack_acm;
rxtx["dvbIsAcm"] = acm.enable_acm;
rxtx["dvbAcmMinModulation"] = extractModcodModulation(acm.min_modcod_acm);
rxtx["dvbAcmMinSpeed"] = extractModcodSpeed(acm.min_modcod_acm);
rxtx["dvbAcmMaxModulation"] = extractModcodModulation(acm.max_modcod_acm);
rxtx["dvbAcmMaxSpeed"] = extractModcodSpeed(acm.max_modcod_acm);
rxtx["dvbSnrReserve"] = acm.snr_threashold_acm; // запас ОСШ
#endif
rxtx["aupcEn"] = acm.enable_aupc;
rxtx["aupcMinAttenuation"] = acm.min_attenuation_aupc;
rxtx["aupcMaxAttenuation"] = acm.max_attenuation_aupc;
rxtx["aupcRequiredSnr"] = acm.snr_threashold_aupc;
#endif
}
#ifdef API_OBJECT_DPDI_SETTINGS_ENABLE
{
auto& dp = res["dpdi"];
dp["isPositional"] = !dpdi.is_delay_window;
#ifdef MODEM_IS_SCPC
dp["searchBandwidth"] = dpdi.freq_offset;
#endif
dp["positionStationLatitude"] = translateCoordinates(dpdi.latitude_station_grad, dpdi.latitude_station_minute);
dp["positionStationLongitude"] = translateCoordinates(dpdi.longitude_station_grad, dpdi.longitude_station_minute);
dp["positionSatelliteLongitude"] = translateCoordinates(dpdi.longitude_sattelite_grad, dpdi.longitude_sattelite_minute);
#ifdef MODEM_IS_SCPC
dp["delayMin"] = dpdi.min_delay;
dp["delayMax"] = dpdi.max_delay;
#else
dp["delay"] = dpdi.max_delay;
#endif
}
#endif
// BucLnb
#ifdef API_OBJECT_BUCLNB_SETTINGS_ENABLE
{
auto& bl = res["buclnb"];
switch (buclnb.buc) {
case voltage_buc::_24V: bl["bucPowering"] = 24; break;
#ifdef MODEM_IS_SCPC
case voltage_buc::_48V: bl["bucPowering"] = 48; break;
#endif
default: bl["bucPowering"] = 0;
}
#ifdef MODEM_IS_TDMA
bl["bucLoKhz"] = buclnb.lo_buc_inkHz;
#endif
bl["bucRefClk10M"] = buclnb.is_ref_10MHz_buc;
switch (buclnb.lnb) {
case voltage_lnb::_13V: bl["lnbPowering"] = 13; break;
case voltage_lnb::_18V: bl["lnbPowering"] = 18; break;
case voltage_lnb::_24V: bl["lnbPowering"] = 24; break;
default: bl["lnbPowering"] = 0;
}
bl["lnbRefClk10M"] = buclnb.is_ref_10MHz_lnb;
#ifdef MODEM_IS_TDMA
bl["lnbLoKhz"] = buclnb.lo_lnb_inkHz;
#endif
bl["srvRefClk10M"] = buclnb.is_ref_10MHz_output;
bl["bucLnbAutoStart"] = buclnb.is_save_current_state;
}
#endif
return res;
}
api_driver::obj::TerminalRxTxSettings::~TerminalRxTxSettings() = default;