summaryrefslogtreecommitdiffstats
path: root/broadcastradio
diff options
context:
space:
mode:
authorTomasz Wasilczyk <twasilczyk@google.com>2017-12-21 11:51:29 -0800
committerTomasz Wasilczyk <twasilczyk@google.com>2018-01-02 11:51:59 -0800
commit8b70ee43b071df7d942ff695d6b87a1de7e3cf29 (patch)
tree6dd15cfebc5d2c8f094f0b2799304c948fa7810b /broadcastradio
parentc0af94ad84e470828c6ad2300353a314bcd7392f (diff)
downloadandroid_hardware_interfaces-8b70ee43b071df7d942ff695d6b87a1de7e3cf29.tar.gz
android_hardware_interfaces-8b70ee43b071df7d942ff695d6b87a1de7e3cf29.tar.bz2
android_hardware_interfaces-8b70ee43b071df7d942ff695d6b87a1de7e3cf29.zip
Implement regional configuration fetching.
Bug: 69958423 Test: VTS Change-Id: I7c184191b4f4999bd03b06bd3b2283e028694918
Diffstat (limited to 'broadcastradio')
-rw-r--r--broadcastradio/2.0/Android.bp5
-rw-r--r--broadcastradio/2.0/IBroadcastRadio.hal22
-rw-r--r--broadcastradio/2.0/default/Android.bp3
-rw-r--r--broadcastradio/2.0/default/BroadcastRadio.cpp52
-rw-r--r--broadcastradio/2.0/default/BroadcastRadio.h7
-rw-r--r--broadcastradio/2.0/default/TunerSession.cpp40
-rw-r--r--broadcastradio/2.0/default/TunerSession.h5
-rw-r--r--broadcastradio/2.0/types.hal103
-rw-r--r--broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp140
-rw-r--r--broadcastradio/common/utils2x/Utils.cpp18
-rw-r--r--broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h19
11 files changed, 396 insertions, 18 deletions
diff --git a/broadcastradio/2.0/Android.bp b/broadcastradio/2.0/Android.bp
index 1d7861e45..f4894ad70 100644
--- a/broadcastradio/2.0/Android.bp
+++ b/broadcastradio/2.0/Android.bp
@@ -16,8 +16,12 @@ hidl_interface {
"android.hidl.base@1.0",
],
types: [
+ "AmFmBandRange",
+ "AmFmRegionConfig",
"ConfigFlag",
"Constants",
+ "DabTableEntry",
+ "Deemphasis",
"IdentifierType",
"Metadata",
"MetadataKey",
@@ -28,6 +32,7 @@ hidl_interface {
"ProgramListChunk",
"ProgramSelector",
"Properties",
+ "Rds",
"Result",
"VendorKeyValue",
],
diff --git a/broadcastradio/2.0/IBroadcastRadio.hal b/broadcastradio/2.0/IBroadcastRadio.hal
index 3ab1cc21d..3b19e6123 100644
--- a/broadcastradio/2.0/IBroadcastRadio.hal
+++ b/broadcastradio/2.0/IBroadcastRadio.hal
@@ -33,6 +33,28 @@ interface IBroadcastRadio {
getProperties() generates (Properties properties);
/**
+ * Fetches current or possible AM/FM region configuration.
+ *
+ * @param full If true, returns full hardware capabilities.
+ * If false, returns current regional configuration.
+ * @return result OK in case of success.
+ * NOT_SUPPORTED if the tuner doesn't support AM/FM.
+ * @return config Hardware capabilities (full=true) or
+ * current configuration (full=false).
+ */
+ getAmFmRegionConfig(bool full)
+ generates (Result result, AmFmRegionConfig config);
+
+ /**
+ * Fetches current DAB region configuration.
+ *
+ * @return result OK in case of success.
+ * NOT_SUPPORTED if the tuner doesn't support DAB.
+ * @return config Current configuration.
+ */
+ getDabRegionConfig() generates (Result result, vec<DabTableEntry> config);
+
+ /**
* Opens a new tuner session.
*
* There may be only one session active at a time. If the new session was
diff --git a/broadcastradio/2.0/default/Android.bp b/broadcastradio/2.0/default/Android.bp
index 6d4effb9f..900454e78 100644
--- a/broadcastradio/2.0/default/Android.bp
+++ b/broadcastradio/2.0/default/Android.bp
@@ -24,6 +24,9 @@ cc_binary {
"-Wextra",
"-Werror",
],
+ cppflags: [
+ "-std=c++1z",
+ ],
srcs: [
"BroadcastRadio.cpp",
"TunerSession.cpp",
diff --git a/broadcastradio/2.0/default/BroadcastRadio.cpp b/broadcastradio/2.0/default/BroadcastRadio.cpp
index 5ab517d17..5dde8a7be 100644
--- a/broadcastradio/2.0/default/BroadcastRadio.cpp
+++ b/broadcastradio/2.0/default/BroadcastRadio.cpp
@@ -33,6 +33,16 @@ using std::map;
using std::mutex;
using std::vector;
+static const AmFmRegionConfig gDefaultAmFmConfig = { //
+ {
+ {87500, 108000, 100, 100}, // FM
+ {153, 282, 3, 9}, // AM LW
+ {531, 1620, 9, 9}, // AM MW
+ {1600, 30000, 1, 5}, // AM SW
+ },
+ static_cast<uint32_t>(Deemphasis::D50),
+ static_cast<uint32_t>(Rds::RDS)};
+
static Properties initProperties(const VirtualRadio& virtualRadio) {
Properties prop = {};
@@ -51,7 +61,9 @@ static Properties initProperties(const VirtualRadio& virtualRadio) {
}
BroadcastRadio::BroadcastRadio(const VirtualRadio& virtualRadio)
- : mVirtualRadio(virtualRadio), mProperties(initProperties(virtualRadio)) {}
+ : mVirtualRadio(virtualRadio),
+ mProperties(initProperties(virtualRadio)),
+ mAmFmConfig(gDefaultAmFmConfig) {}
Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb) {
ALOGV("%s", __func__);
@@ -59,6 +71,44 @@ Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb) {
return {};
}
+AmFmRegionConfig BroadcastRadio::getAmFmConfig() const {
+ lock_guard<mutex> lk(mMut);
+ return mAmFmConfig;
+}
+
+Return<void> BroadcastRadio::getAmFmRegionConfig(bool full, getAmFmRegionConfig_cb _hidl_cb) {
+ ALOGV("%s(%d)", __func__, full);
+
+ if (full) {
+ AmFmRegionConfig config = {};
+ config.ranges = hidl_vec<AmFmBandRange>({
+ {65000, 108000, 10, 0}, // FM
+ {150, 30000, 1, 0}, // AM
+ });
+ config.fmDeemphasis = Deemphasis::D50 | Deemphasis::D75;
+ config.fmRds = Rds::RDS | Rds::RBDS;
+ _hidl_cb(Result::OK, config);
+ return {};
+ } else {
+ _hidl_cb(Result::OK, getAmFmConfig());
+ return {};
+ }
+}
+
+Return<void> BroadcastRadio::getDabRegionConfig(getDabRegionConfig_cb _hidl_cb) {
+ ALOGV("%s", __func__);
+
+ hidl_vec<DabTableEntry> config = {
+ {"5A", 174928}, {"7D", 194064}, {"8A", 195936}, {"8B", 197648}, {"9A", 202928},
+ {"9B", 204640}, {"9C", 206352}, {"10B", 211648}, {"10C", 213360}, {"10D", 215072},
+ {"11A", 216928}, {"11B", 218640}, {"11C", 220352}, {"11D", 222064}, {"12A", 223936},
+ {"12B", 225648}, {"12C", 227360}, {"12D", 229072},
+ };
+
+ _hidl_cb(Result::OK, config);
+ return {};
+}
+
Return<void> BroadcastRadio::openSession(const sp<ITunerCallback>& callback,
openSession_cb _hidl_cb) {
ALOGV("%s", __func__);
diff --git a/broadcastradio/2.0/default/BroadcastRadio.h b/broadcastradio/2.0/default/BroadcastRadio.h
index fcf06155e..733cadf8a 100644
--- a/broadcastradio/2.0/default/BroadcastRadio.h
+++ b/broadcastradio/2.0/default/BroadcastRadio.h
@@ -32,14 +32,19 @@ struct BroadcastRadio : public IBroadcastRadio {
// V2_0::IBroadcastRadio methods
Return<void> getProperties(getProperties_cb _hidl_cb) override;
+ Return<void> getAmFmRegionConfig(bool full, getAmFmRegionConfig_cb _hidl_cb);
+ Return<void> getDabRegionConfig(getDabRegionConfig_cb _hidl_cb);
Return<void> openSession(const sp<ITunerCallback>& callback, openSession_cb _hidl_cb) override;
Return<void> getImage(uint32_t id, getImage_cb _hidl_cb);
std::reference_wrapper<const VirtualRadio> mVirtualRadio;
Properties mProperties;
+ AmFmRegionConfig getAmFmConfig() const;
+
private:
- std::mutex mMut;
+ mutable std::mutex mMut;
+ AmFmRegionConfig mAmFmConfig;
wp<TunerSession> mSession;
};
diff --git a/broadcastradio/2.0/default/TunerSession.cpp b/broadcastradio/2.0/default/TunerSession.cpp
index 244544a00..3166d8619 100644
--- a/broadcastradio/2.0/default/TunerSession.cpp
+++ b/broadcastradio/2.0/default/TunerSession.cpp
@@ -77,8 +77,12 @@ void TunerSession::tuneInternalLocked(const ProgramSelector& sel) {
mCallback->onCurrentProgramInfoChanged(programInfo);
}
+const BroadcastRadio& TunerSession::module() const {
+ return mModule.get();
+}
+
const VirtualRadio& TunerSession::virtualRadio() const {
- return mModule.get().mVirtualRadio;
+ return module().mVirtualRadio;
}
Return<Result> TunerSession::tune(const ProgramSelector& sel) {
@@ -86,7 +90,7 @@ Return<Result> TunerSession::tune(const ProgramSelector& sel) {
lock_guard<mutex> lk(mMut);
if (mIsClosed) return Result::INVALID_STATE;
- if (!utils::isSupported(mModule.get().mProperties, sel)) {
+ if (!utils::isSupported(module().mProperties, sel)) {
ALOGW("Selector not supported");
return Result::NOT_SUPPORTED;
}
@@ -170,23 +174,19 @@ Return<Result> TunerSession::step(bool directionUp) {
mIsTuneCompleted = false;
auto stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY);
-#if 0
- // TODO(b/69958423): handle regions
- if (directionUp) {
- stepTo += mAmfmConfig.spacings[0];
- } else {
- stepTo -= mAmfmConfig.spacings[0];
+ auto range = getAmFmRangeLocked();
+ if (!range) {
+ ALOGE("Can't find current band");
+ return Result::INTERNAL_ERROR;
}
- if (stepTo > mAmfmConfig.upperLimit) stepTo = mAmfmConfig.lowerLimit;
- if (stepTo < mAmfmConfig.lowerLimit) stepTo = mAmfmConfig.upperLimit;
-#else
if (directionUp) {
- stepTo += 100;
+ stepTo += range->spacing;
} else {
- stepTo -= 100;
+ stepTo -= range->spacing;
}
-#endif
+ if (stepTo > range->upperBound) stepTo = range->lowerBound;
+ if (stepTo < range->lowerBound) stepTo = range->upperBound;
auto task = [this, stepTo]() {
ALOGI("Performing step to %s", std::to_string(stepTo).c_str());
@@ -280,6 +280,18 @@ Return<void> TunerSession::close() {
return {};
}
+std::optional<AmFmBandRange> TunerSession::getAmFmRangeLocked() const {
+ if (!mIsTuneCompleted) return {};
+ if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) return {};
+
+ auto freq = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY);
+ for (auto&& range : module().getAmFmConfig().ranges) {
+ if (range.lowerBound <= freq && range.upperBound >= freq) return range;
+ }
+
+ return {};
+}
+
} // namespace implementation
} // namespace V2_0
} // namespace broadcastradio
diff --git a/broadcastradio/2.0/default/TunerSession.h b/broadcastradio/2.0/default/TunerSession.h
index a58aa1952..5d27b1e6d 100644
--- a/broadcastradio/2.0/default/TunerSession.h
+++ b/broadcastradio/2.0/default/TunerSession.h
@@ -22,6 +22,8 @@
#include <android/hardware/broadcastradio/2.0/ITunerSession.h>
#include <broadcastradio-utils/WorkerThread.h>
+#include <optional>
+
namespace android {
namespace hardware {
namespace broadcastradio {
@@ -48,6 +50,8 @@ struct TunerSession : public ITunerSession {
getParameters_cb _hidl_cb) override;
virtual Return<void> close() override;
+ std::optional<AmFmBandRange> getAmFmRangeLocked() const;
+
private:
std::mutex mMut;
WorkerThread mThread;
@@ -61,6 +65,7 @@ struct TunerSession : public ITunerSession {
void tuneInternalLocked(const ProgramSelector& sel);
const VirtualRadio& virtualRadio() const;
+ const BroadcastRadio& module() const;
};
} // namespace implementation
diff --git a/broadcastradio/2.0/types.hal b/broadcastradio/2.0/types.hal
index b5264f47a..38a5709a2 100644
--- a/broadcastradio/2.0/types.hal
+++ b/broadcastradio/2.0/types.hal
@@ -32,6 +32,7 @@ enum Constants : int32_t {
enum Result : int32_t {
OK,
UNKNOWN_ERROR,
+ INTERNAL_ERROR,
INVALID_ARGUMENTS,
INVALID_STATE,
NOT_SUPPORTED,
@@ -120,6 +121,108 @@ struct VendorKeyValue {
};
/**
+ * A supported or configured RDS variant.
+ *
+ * Both might be set for hardware capabilities check (with full=true when
+ * calling getAmFmRegionConfig), but only one (or none) for specific
+ * region settings.
+ */
+enum Rds : uint8_t {
+ /** Standard variant, used everywhere except North America. */
+ RDS = 1 << 0,
+
+ /** Variant used in North America. */
+ RBDS = 1 << 1,
+};
+
+/**
+ * FM de-emphasis filter supported or configured.
+ *
+ * Both might be set for hardware capabilities check (with full=true when
+ * calling getAmFmRegionConfig), but exactly one for specific region settings.
+ */
+enum Deemphasis : uint8_t {
+ D50 = 1 << 0,
+ D75 = 1 << 1,
+};
+
+/**
+ * Regional configuration for AM/FM.
+ *
+ * For hardware capabilities check (with full=true when calling
+ * getAmFmRegionConfig), HAL implementation fills entire supported range of
+ * frequencies and features.
+ *
+ * When checking current configuration, at most one bit in each bitfield
+ * can be set.
+ */
+struct AmFmRegionConfig {
+ /**
+ * All supported or configured AM/FM bands.
+ *
+ * AM/FM bands are identified by frequency value
+ * (see IdentifierType::AMFM_FREQUENCY).
+ *
+ * With typical configuration, it's expected to have two frequency ranges
+ * for capabilities check (AM and FM) and four ranges for specific region
+ * configuration (AM LW, AM MW, AM SW, FM).
+ */
+ vec<AmFmBandRange> ranges;
+
+ /** De-emphasis filter supported/configured. */
+ bitfield<Deemphasis> fmDeemphasis;
+
+ /** RDS/RBDS variant supported/configured. */
+ bitfield<Rds> fmRds;
+};
+
+/**
+ * AM/FM band range for region configuration.
+ *
+ * Defines channel grid: each possible channel is set at
+ * lowerBound + channelNumber * spacing, up to upperBound.
+ */
+struct AmFmBandRange {
+ /** The frequency of the first channel within the range. */
+ uint32_t lowerBound;
+
+ /** The frequency of the last channel within the range. */
+ uint32_t upperBound;
+
+ /** Channel grid resolution, how far apart are the channels. */
+ uint32_t spacing;
+
+ /**
+ * Spacing used when scanning for channels. It's a multiply of spacing and
+ * allows to skip some channels when scanning to make it faster.
+ *
+ * Tuner may first quickly check every n-th channel and if it detects echo
+ * from a station, it fine-tunes to find the exact frequency.
+ *
+ * It's ignored for capabilities check (with full=true when calling
+ * getAmFmRegionConfig).
+ */
+ uint32_t scanSpacing;
+};
+
+/**
+ * An entry in regional configuration for DAB.
+ *
+ * Defines a frequency table row for ensembles.
+ */
+struct DabTableEntry {
+ /**
+ * Channel name, i.e. 5A, 7B.
+ *
+ * It must match the following regular expression: /^[A-Z0-9]{2,5}$/.
+ */
+ string label;
+
+ /** Frequency, in kHz. */
+ uint32_t frequency;
+};
+
+/**
* Properties of a given broadcast radio module.
*/
struct Properties {
diff --git a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
index cbe628840..87ac93415 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -26,9 +26,11 @@
#include <broadcastradio-vts-utils/call-barrier.h>
#include <broadcastradio-vts-utils/mock-timeout.h>
#include <broadcastradio-vts-utils/pointer-utils.h>
+#include <cutils/bitops.h>
#include <gmock/gmock.h>
#include <chrono>
+#include <regex>
namespace android {
namespace hardware {
@@ -93,6 +95,7 @@ class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
virtual void TearDown() override;
bool openSession();
+ bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
sp<IBroadcastRadio> mModule;
Properties mProperties;
@@ -159,6 +162,22 @@ bool BroadcastRadioHalTest::openSession() {
return nullptr != mSession.get();
}
+bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
+ auto halResult = Result::UNKNOWN_ERROR;
+ auto cb = [&](Result result, AmFmRegionConfig configCb) {
+ halResult = result;
+ if (config) *config = configCb;
+ };
+
+ auto hidlResult = mModule->getAmFmRegionConfig(full, cb);
+ EXPECT_TRUE(hidlResult.isOk());
+
+ if (halResult == Result::NOT_SUPPORTED) return false;
+
+ EXPECT_EQ(Result::OK, halResult);
+ return halResult == Result::OK;
+}
+
/**
* Test session opening.
*
@@ -181,6 +200,127 @@ TEST_F(BroadcastRadioHalTest, OpenSession) {
ASSERT_TRUE(openSession());
}
+static bool isValidAmFmFreq(uint64_t freq) {
+ auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq);
+ return utils::isValid(id);
+}
+
+static void validateRange(const AmFmBandRange& range) {
+ EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
+ EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
+ EXPECT_LT(range.lowerBound, range.upperBound);
+ EXPECT_GT(range.spacing, 0u);
+ EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing);
+}
+
+static bool supportsFM(const AmFmRegionConfig& config) {
+ for (auto&& range : config.ranges) {
+ if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true;
+ }
+ return false;
+}
+
+/**
+ * Test fetching AM/FM regional configuration.
+ *
+ * Verifies that:
+ * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
+ * - there is at least one AM/FM band configured;
+ * - FM Deemphasis and RDS are correctly configured for FM-capable radio;
+ * - all channel grids (frequency ranges and spacings) are valid;
+ * - scan spacing is a multiply of manual spacing value.
+ */
+TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfig) {
+ AmFmRegionConfig config;
+ bool supported = getAmFmRegionConfig(false, &config);
+ if (!supported) {
+ printSkipped("AM/FM not supported");
+ return;
+ }
+
+ EXPECT_GT(config.ranges.size(), 0u);
+ EXPECT_LE(popcountll(config.fmDeemphasis), 1);
+ EXPECT_LE(popcountll(config.fmRds), 1);
+
+ for (auto&& range : config.ranges) {
+ validateRange(range);
+ EXPECT_EQ(0u, range.scanSpacing % range.spacing);
+ EXPECT_GE(range.scanSpacing, range.spacing);
+ }
+
+ if (supportsFM(config)) {
+ EXPECT_EQ(popcountll(config.fmDeemphasis), 1);
+ }
+}
+
+/**
+ * Test fetching AM/FM regional capabilities.
+ *
+ * Verifies that:
+ * - AM/FM regional capabilities are either available or not supported at all by the hardware;
+ * - there is at least one AM/FM range supported;
+ * - there is at least one de-emphasis filter mode supported for FM-capable radio;
+ * - all channel grids (frequency ranges and spacings) are valid;
+ * - scan spacing is not set.
+ */
+TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) {
+ AmFmRegionConfig config;
+ bool supported = getAmFmRegionConfig(true, &config);
+ if (!supported) {
+ printSkipped("AM/FM not supported");
+ return;
+ }
+
+ EXPECT_GT(config.ranges.size(), 0u);
+
+ for (auto&& range : config.ranges) {
+ validateRange(range);
+ EXPECT_EQ(0u, range.scanSpacing);
+ }
+
+ if (supportsFM(config)) {
+ EXPECT_GE(popcountll(config.fmDeemphasis), 1);
+ }
+}
+
+/**
+ * Test fetching DAB regional configuration.
+ *
+ * Verifies that:
+ * - DAB regional configuration is either set at startup or not supported at all by the hardware;
+ * - all channel labels match correct format;
+ * - all channel frequencies are in correct range.
+ */
+TEST_F(BroadcastRadioHalTest, GetDabRegionConfig) {
+ Result halResult;
+ hidl_vec<DabTableEntry> config;
+ auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
+ halResult = result;
+ config = configCb;
+ };
+ auto hidlResult = mModule->getDabRegionConfig(cb);
+ ASSERT_TRUE(hidlResult.isOk());
+
+ if (halResult == Result::NOT_SUPPORTED) {
+ printSkipped("DAB not supported");
+ return;
+ }
+ ASSERT_EQ(Result::OK, halResult);
+
+ std::regex re("^[A-Z0-9]{2,5}$");
+ // double-check correctness of the test
+ ASSERT_TRUE(std::regex_match("5A", re));
+ ASSERT_FALSE(std::regex_match("5a", re));
+ ASSERT_FALSE(std::regex_match("123ABC", re));
+
+ for (auto&& entry : config) {
+ EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
+
+ auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency);
+ EXPECT_TRUE(utils::isValid(id));
+ }
+}
+
/**
* Test tuning with FM selector.
*
diff --git a/broadcastradio/common/utils2x/Utils.cpp b/broadcastradio/common/utils2x/Utils.cpp
index e0337b41a..d825a7a00 100644
--- a/broadcastradio/common/utils2x/Utils.cpp
+++ b/broadcastradio/common/utils2x/Utils.cpp
@@ -89,6 +89,18 @@ IdentifierIterator end(const V2_0::ProgramSelector& sel) {
return IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
}
+FrequencyBand getBand(uint64_t freq) {
+ // keep in sync with
+ // frameworks/base/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
+ if (freq < 30) return FrequencyBand::UNKNOWN;
+ if (freq < 500) return FrequencyBand::AM_LW;
+ if (freq < 1705) return FrequencyBand::AM_MW;
+ if (freq < 30000) return FrequencyBand::AM_SW;
+ if (freq < 60000) return FrequencyBand::UNKNOWN;
+ if (freq < 110000) return FrequencyBand::FM;
+ return FrequencyBand::UNKNOWN;
+}
+
static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
const IdentifierType type) {
return hasId(a, type) && hasId(b, type);
@@ -194,7 +206,7 @@ bool isSupported(const Properties& prop, const ProgramSelector& sel) {
return false;
}
-static bool isValid(const ProgramIdentifier& id) {
+bool isValid(const ProgramIdentifier& id) {
auto val = id.value;
bool valid = true;
@@ -209,8 +221,10 @@ static bool isValid(const ProgramIdentifier& id) {
case IdentifierType::INVALID:
expect(false, "IdentifierType::INVALID");
break;
- case IdentifierType::AMFM_FREQUENCY:
case IdentifierType::DAB_FREQUENCY:
+ expect(val > 100000u, "f > 100MHz");
+ // fallthrough
+ case IdentifierType::AMFM_FREQUENCY:
case IdentifierType::DRMO_FREQUENCY:
expect(val > 100u, "f > 100kHz");
expect(val < 10000000u, "f < 10GHz");
diff --git a/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h b/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
index bac11fd02..e3134f7b8 100644
--- a/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
+++ b/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
@@ -27,6 +27,14 @@ namespace hardware {
namespace broadcastradio {
namespace utils {
+enum class FrequencyBand {
+ UNKNOWN,
+ FM,
+ AM_LW,
+ AM_MW,
+ AM_SW,
+};
+
V2_0::IdentifierType getType(uint32_t typeAsInt);
V2_0::IdentifierType getType(const V2_0::ProgramIdentifier& id);
@@ -64,6 +72,16 @@ IdentifierIterator begin(const V2_0::ProgramSelector& sel);
IdentifierIterator end(const V2_0::ProgramSelector& sel);
/**
+ * Guesses band from the frequency value.
+ *
+ * The band bounds are not exact to cover multiple regions.
+ * The function is biased towards success, i.e. it never returns
+ * FrequencyBand::UNKNOWN for correct frequency, but a result for
+ * incorrect one is undefined (it doesn't have to return UNKNOWN).
+ */
+FrequencyBand getBand(uint64_t frequency);
+
+/**
* Checks, if {@code pointer} tunes to {@channel}.
*
* For example, having a channel {AMFM_FREQUENCY = 103.3}:
@@ -105,6 +123,7 @@ std::vector<uint64_t> getAllIds(const V2_0::ProgramSelector& sel, const V2_0::Id
*/
bool isSupported(const V2_0::Properties& prop, const V2_0::ProgramSelector& sel);
+bool isValid(const V2_0::ProgramIdentifier& id);
bool isValid(const V2_0::ProgramSelector& sel);
V2_0::ProgramIdentifier make_identifier(V2_0::IdentifierType type, uint64_t value);