blob: a198937ec54018eaa0ab19ceff8a7bcf5abef16a [file] [log] [blame]
Ericc0f78a32023-05-12 13:00:14 +02001#pragma once
2/*
3 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * Author: Eric Wild <ewild@sysmocom.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <string>
24#include <tuple>
25
26#include "Logger.h"
27
28extern "C" {
29#include <osmocom/gsm/gsm_utils.h>
30}
31
32template <typename powermapt, typename devmapt>
33class band_manager {
34 using powerkeyt = typename powermapt::key_type;
35 using powermappedt = typename powermapt::mapped_type;
36 using devkeyt = typename devmapt::key_type;
37 const devkeyt &m_dev_type;
38 const powermapt &m_power_map;
39 const devmapt &m_dev_map;
40 powerkeyt m_fallback;
41 enum gsm_band m_band;
42 powermappedt m_band_desc;
43 bool band_ass_curr_sess{}; /* true if "band" was set after last POWEROFF */
44
45 // looks up either first tuple element (->enum) or straight enum
46 template <typename T, typename std::enable_if<std::is_enum<T>::value>::type *dummy = nullptr>
47 auto key_helper(T &t) -> T
48 {
49 return t;
50 }
51
52 template <typename T>
53 auto key_helper(T t) -> typename std::tuple_element<0, T>::type
54 {
55 return std::get<0>(t);
56 }
57
58 void assign_band_desc(enum gsm_band req_band)
59 {
60 auto key = key_helper(m_dev_type);
61 auto fallback_key = key_helper(m_fallback);
62 auto it = m_power_map.find({ key, req_band });
63 if (it == m_power_map.end()) {
64 auto desc = m_dev_map.at(m_dev_type);
65 LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device " << desc.desc_str
66 << " on band " << gsm_band_name(req_band) << ", using fallback..";
67 it = m_power_map.find({ fallback_key, req_band });
68 }
69 OSMO_ASSERT(it != m_power_map.end());
70 m_band_desc = it->second;
71 }
72
73 bool set_band(enum gsm_band req_band)
74 {
75 if (band_ass_curr_sess && req_band != m_band) {
76 LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
77 << " different from previous band " << gsm_band_name(m_band);
78 return false;
79 }
80
81 if (req_band != m_band) {
82 m_band = req_band;
83 assign_band_desc(m_band);
84 }
85 band_ass_curr_sess = true;
86 return true;
87 }
88
89 public:
90 band_manager(const devkeyt &dev_type, const powermapt &power_map, const devmapt &dev_map, powerkeyt fallback)
91 : m_dev_type(dev_type), m_power_map(power_map), m_dev_map(dev_map), m_fallback(fallback),
92 m_band((enum gsm_band)0)
93 {
94 }
95 band_manager(const powermapt &power_map, const devmapt &dev_map)
96 : m_dev_type(dev_map.begin()->first), m_power_map(power_map), m_dev_map(dev_map),
97 m_fallback(m_power_map.begin()->first), m_band((enum gsm_band)0)
98 {
99 }
100 void band_reset()
101 {
102 band_ass_curr_sess = false;
103 }
104
105 void get_dev_band_desc(powermappedt &desc)
106 {
107 if (m_band == 0) {
108 LOGC(DDEV, ERROR)
109 << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
110 assign_band_desc(GSM_BAND_900);
111 }
112 desc = m_band_desc;
113 }
114
115 bool update_band_from_freq(double wFreq, int chan, bool is_tx)
116 {
117 enum gsm_band req_band;
118 auto dirstr = is_tx ? "Tx" : "Rx";
119 auto req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, !is_tx);
120 if (req_arfcn == 0xffff) {
121 LOGCHAN(chan, DDEV, ALERT)
122 << "Unknown ARFCN for " << dirstr << " Frequency " << wFreq / 1000 << " kHz";
123 return false;
124 }
125 if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
126 LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for " << dirstr << " Frequency " << wFreq
127 << " Hz (ARFCN " << req_arfcn << " )";
128 return false;
129 }
130
131 return set_band(req_band);
132 }
133};