#pragma once /* * (C) 2022 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Eric Wild * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include "Logger.h" extern "C" { #include } template class band_manager { using powerkeyt = typename powermapt::key_type; using powermappedt = typename powermapt::mapped_type; using devkeyt = typename devmapt::key_type; devkeyt m_dev_type; const powermapt &m_power_map; const devmapt &m_dev_map; powerkeyt m_fallback; enum gsm_band m_band; powermappedt m_band_desc; bool band_ass_curr_sess{}; /* true if "band" was set after last POWEROFF */ // looks up either first tuple element (->enum) or straight enum template ::value>::type *dummy = nullptr> auto key_helper(T &t) -> T { return t; } template auto key_helper(T t) -> typename std::tuple_element<0, T>::type { return std::get<0>(t); } void assign_band_desc(enum gsm_band req_band) { auto key = key_helper(m_dev_type); auto fallback_key = key_helper(m_fallback); auto it = m_power_map.find({ key, req_band }); if (it == m_power_map.end()) { auto desc = m_dev_map.at(m_dev_type); LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device " << desc.desc_str << " on band " << gsm_band_name(req_band) << ", using fallback.."; it = m_power_map.find({ fallback_key, req_band }); } OSMO_ASSERT(it != m_power_map.end()); m_band_desc = it->second; } bool set_band(enum gsm_band req_band) { if (band_ass_curr_sess && req_band != m_band) { LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band) << " different from previous band " << gsm_band_name(m_band); return false; } if (req_band != m_band) { m_band = req_band; assign_band_desc(m_band); } band_ass_curr_sess = true; return true; } public: band_manager(const devkeyt &dev_type, const powermapt &power_map, const devmapt &dev_map, powerkeyt fallback) : m_dev_type(dev_type), m_power_map(power_map), m_dev_map(dev_map), m_fallback(fallback), m_band((enum gsm_band)0) { } band_manager(const powermapt &power_map, const devmapt &dev_map) : m_dev_type(dev_map.begin()->first), m_power_map(power_map), m_dev_map(dev_map), m_fallback(m_power_map.begin()->first), m_band((enum gsm_band)0) { } void band_reset() { band_ass_curr_sess = false; } void update_band_dev(devkeyt dev_type) { m_dev_type = dev_type; } void get_dev_band_desc(powermappedt &desc) { if (m_band == 0) { LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default..."; assign_band_desc(GSM_BAND_900); } desc = m_band_desc; } bool update_band_from_freq(double wFreq, int chan, bool is_tx) { enum gsm_band req_band; auto dirstr = is_tx ? "Tx" : "Rx"; auto req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, !is_tx); if (req_arfcn == 0xffff) { LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for " << dirstr << " Frequency " << wFreq / 1000 << " kHz"; return false; } if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) { LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for " << dirstr << " Frequency " << wFreq << " Hz (ARFCN " << req_arfcn << " )"; return false; } return set_band(req_band); } };