Radio: Add parameter validation, add units

This commit is contained in:
Dominic Höglinger 2025-09-21 20:35:16 +02:00
parent 4ac4507538
commit 5eb3dbcd9f
8 changed files with 226 additions and 53 deletions

View File

@ -33,5 +33,4 @@ public:
virtual bool start(const Modulation modulation) override;
virtual bool stop() override;
};

View File

@ -5,94 +5,146 @@
constexpr const char* TAG = "Sx1262";
template<typename T>
static constexpr Sx1262::ParameterStatus checkLimitsAndApply(T &target, const float value, const float lower, const float upper, const unsigned step = 0) {
if ((value >= lower) && (value <= upper)) {
if (step != 0) {
int ivalue = static_cast<int>(value);
if ((ivalue % step) != 0) {
return Sx1262::ParameterStatus::ValueError;
}
}
target = static_cast<T>(value);
return Sx1262::ParameterStatus::Success;
}
return Sx1262::ParameterStatus::ValueError;
}
template<typename T>
static constexpr Sx1262::ParameterStatus checkValuesAndApply(T &target, const float value, std::initializer_list<float> valids) {
for (float valid : valids) {
if (value == valid) {
target = static_cast<T>(value);
return Sx1262::ParameterStatus::Success;
}
}
return Sx1262::ParameterStatus::ValueError;
}
void IRAM_ATTR dio1handler(void* context) {
((Sx1262*)context)->dio1Event();
}
bool Sx1262::setParameter(const Parameter parameter, const float value) {
Sx1262::ParameterStatus Sx1262::setParameter(const Parameter parameter, const float value) {
using enum Parameter;
switch (parameter) {
case Power:
power = value;
return true;
return checkLimitsAndApply(power, value, -9.0, 22.0);
case Frequency:
frequency = value;
return true;
return checkLimitsAndApply(frequency, value, 150.0, 960.0);
case Bandwidth:
bandwidth = value;
return true;
return checkValuesAndApply(bandwidth, value, {
//LoRa
7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0, 500.0,
// FSK
4.8, 5.8, 7.3, 9.7, 11.7, 14.6, 19.5, 23.4, 29.3, 39.0, 46.9, 58.6, 78.2,
// LR-FHSS
39.06, 85.94, 136.72, 183.59, 335.94, 386.72, 722.66, 773.44, 1523.4, 1574.2
});
case SpreadFactor:
spreadFactor = value;
return true;
return checkLimitsAndApply(spreadFactor, value, 7.0, 12.0, 1);
case CodingRate:
codingRate = value;
return true;
return checkLimitsAndApply(codingRate, value, 5.0, 8.0, 1);
case SyncWord:
syncWord = value;
return true;
return checkLimitsAndApply(syncWord, value, 0.0, 255.0, 1);
case PreambleLength:
preambleLength = value;
return true;
return checkLimitsAndApply(preambleLength, value, 0.0, 65535.0, 1);
case DataRate:
bitRate = value;
return true;
return checkLimitsAndApply(bitRate, value, 0.6, 300.0);
case FrequencyDeviation:
frequencyDeviation = value;
return true;
return checkLimitsAndApply(frequencyDeviation, value, 0.0, 200.0);
case NarrowGrid:
narrowGrid = value;
return true;
return checkLimitsAndApply(narrowGrid, value, 0.0, 1.0, 1);
default:
break;
}
TT_LOG_W(TAG, "Tried to set unsupported parameter \"%s\" to %f", toString(parameter), value);
return false;
return Sx1262::ParameterStatus::NotDefined;
}
bool Sx1262::getParameter(const Parameter parameter, float &value) const {
Sx1262::ParameterStatus Sx1262::getParameter(const Parameter parameter, float &value) const {
using enum Parameter;
switch (parameter) {
case Power:
value = power;
return true;
return Sx1262::ParameterStatus::Success;
case Frequency:
value = frequency;
return true;
return Sx1262::ParameterStatus::Success;
case Bandwidth:
value = bandwidth;
return true;
return Sx1262::ParameterStatus::Success;
case SpreadFactor:
value = spreadFactor;
return true;
return Sx1262::ParameterStatus::Success;
case CodingRate:
value = codingRate;
return true;
return Sx1262::ParameterStatus::Success;
case SyncWord:
value = syncWord;
return true;
return Sx1262::ParameterStatus::Success;
case PreambleLength:
value = preambleLength;
return true;
return Sx1262::ParameterStatus::Success;
case DataRate:
value = bitRate;
return true;
return Sx1262::ParameterStatus::Success;
case FrequencyDeviation:
value = frequencyDeviation;
return true;
return Sx1262::ParameterStatus::Success;
case NarrowGrid:
value = narrowGrid;
return true;
return Sx1262::ParameterStatus::Success;
default:
break;
}
TT_LOG_W(TAG, "Tried to get unsupported parameter \"%s\"", toString(parameter));
return Sx1262::ParameterStatus::NotDefined;
}
return false;
tt::hal::radio::Unit Sx1262::getParameterUnit(const Parameter parameter) const {
using enum Parameter;
using Unit = tt::hal::radio::Unit;
switch (parameter) {
case Power:
return Unit(Unit::Name::DecibelMilliwatts);
case Frequency:
return Unit(Unit::Prefix::Kilo, Unit::Name::Herz);
case Bandwidth:
return Unit(Unit::Prefix::Kilo, Unit::Name::Herz);
case SpreadFactor:
case CodingRate: // no break
case SyncWord: // no break
case PreambleLength: // no break
return Unit(Unit::Name::None);
case DataRate:
return Unit(Unit::Prefix::Kilo, Unit::Name::BitsPerSecond);
case FrequencyDeviation:
return Unit(Unit::Prefix::Kilo, Unit::Name::Herz);
case NarrowGrid:
return Unit(Unit::Name::None);
default:
break;
}
TT_LOG_W(TAG, "Tried to get unit for unsupported parameter \"%s\"", toString(parameter));
return Unit(Unit::Name::None);
}
void Sx1262::registerDio1Isr() {

View File

@ -82,8 +82,9 @@ public:
std::string getDescription() const override { return "Semtech SX1262 LoRa, FSK and LR-FHSS capable radio"; }
bool setParameter(const Parameter parameter, const float value) override;
bool getParameter(const Parameter parameter, float &value) const override;
ParameterStatus setParameter(const Parameter parameter, const float value) override;
ParameterStatus getParameter(const Parameter parameter, float &value) const override;
tt::hal::radio::Unit getParameterUnit(const Parameter parameter) const override;
bool canTransmit(const Modulation modulation) override {
return (modulation == Modulation::Fsk) ||

View File

@ -34,23 +34,29 @@ public:
}
return false;
}
void clear() { parameters.clear(); }
bool apply(RadioDevice &radio) {
bool successful = true;
for (const auto& [parameter, value] : parameters) {
// No break on error chosen to apply all parameters,
// a bad one doesn't make the successive tries any more invalid
successful &= radio.setParameter(parameter, value);
successful &= (radio.setParameter(parameter, value) == RadioDevice::ParameterStatus::Success);
}
return successful;
}
bool load(const RadioDevice &radio) {
bool successful = true;
for (const auto& [parameter, value] : parameters) {
successful &= radio.getParameter(parameter, parameters[parameter]);
void load(const RadioDevice &radio) {
// This loop has to be ajusted for each new parameter.
// Could be made more maintainable with a template enum iterator in an utility header.
for (RadioDevice::Parameter p = RadioDevice::Parameter::Power;
p < RadioDevice::Parameter::NarrowGrid;
p = static_cast<RadioDevice::Parameter>((size_t)p + 1)) {
float value = 0.0;
if (radio.getParameter(p, value) == RadioDevice::ParameterStatus::Success) {
set(p, value);
}
}
return successful;
}
};

View File

@ -1,6 +1,7 @@
#pragma once
#include "../Device.h"
#include "Unit.h"
#include <Tactility/Mutex.h>
#include <Tactility/Thread.h>
@ -45,6 +46,12 @@ public:
NarrowGrid
};
enum class ParameterStatus {
NotDefined,
ValueError,
Success
};
typedef int RxSubscriptionId;
typedef int TxId;
@ -120,8 +127,9 @@ public:
Type getType() const override { return Type::Radio; }
virtual bool setParameter(const Parameter parameter, const float value) = 0;
virtual bool getParameter(const Parameter parameter, float &value) const = 0;
virtual ParameterStatus setParameter(const Parameter parameter, const float value) = 0;
virtual ParameterStatus getParameter(const Parameter parameter, float &value) const = 0;
virtual Unit getParameterUnit(const Parameter parameter) const = 0;
virtual bool canTransmit(const Modulation modulation) = 0;
virtual bool canReceive(const Modulation modulation) = 0;

View File

@ -0,0 +1,46 @@
#pragma once
#include <string>
namespace tt::hal::radio {
class Unit {
public:
enum class Prefix {
Femto,
Pico,
Nano,
Milli,
None,
Kilo,
Mega,
Giga,
Terra,
Peta
};
enum class Name
{
None,
BitsPerSecond,
BytesPerSecond,
Herz,
Decibel,
DecibelMilliwatts
};
const Prefix prefix;
const Name unit;
explicit Unit(const Prefix si, const Name unit)
: prefix(si), unit(unit) {}
explicit Unit(const Name unit)
: prefix(Prefix::None), unit(unit) {}
std::string toString() const;
};
const char* toString(Unit::Prefix prefix);
const char* toString(Unit::Name unit);
}

View File

@ -428,17 +428,18 @@ public:
void configureFromForm() {
using enum tt::hal::radio::RadioDevice::Parameter;
using enum tt::hal::radio::RadioDevice::ParameterStatus;
std::string buffer;
int value = 0;
bool configured = true;
buffer = lv_textarea_get_text(frequencyInput);
if (!buffer.empty()) {
configured &= loraDevice->setParameter(Frequency, std::stof(buffer));
configured &= (loraDevice->setParameter(Frequency, std::stof(buffer)) == Success);
}
buffer = lv_textarea_get_text(bandwidthInput);
if (!buffer.empty()) {
configured &= loraDevice->setParameter(Bandwidth, std::stof(buffer));
configured &= (loraDevice->setParameter(Bandwidth, std::stof(buffer)) == Success);
}
buffer = lv_textarea_get_text(syncwordInput);
if (!buffer.empty()) {
@ -446,20 +447,20 @@ public:
std::stringstream ss(buffer);
ss >> std::hex >> syncWord;
configured &= loraDevice->setParameter(SyncWord, std::stoi(buffer, nullptr, 16));
configured &= (loraDevice->setParameter(SyncWord, std::stoi(buffer, nullptr, 16)) == Success);
}
value = lv_slider_get_value(deBitsInput);
configured &= loraDevice->setParameter(CodingRate, value);
configured &= (loraDevice->setParameter(CodingRate, value) == Success);
value = lv_slider_get_value(sfInput);
configured &= loraDevice->setParameter(SpreadFactor, value);
configured &= (loraDevice->setParameter(SpreadFactor, value) == Success);
value = lv_slider_get_value(preambleChirpsInput);
configured &= loraDevice->setParameter(PreambleLength, value);
configured &= (loraDevice->setParameter(PreambleLength, value) == Success);
buffer = lv_textarea_get_text(txPowInput);
if (!buffer.empty()) {
configured &= loraDevice->setParameter(Power, std::stof(buffer));
configured &= (loraDevice->setParameter(Power, std::stof(buffer)) == Success);
}
}

View File

@ -0,0 +1,60 @@
#include "Tactility/hal/radio/Unit.h"
#include <cstring>
namespace tt::hal::radio {
std::string Unit::toString() const {
return std::string(toString(prefix))+std::string(toString(unit));
}
const char* toString(Unit::Prefix prefix) {
using enum Unit::Prefix;
switch (prefix) {
case Femto:
return "f";
case Pico:
return "p";
case Nano:
return "n";
case Milli:
return "m";
case None:
return "";
case Kilo:
return "k";
case Mega:
return "M";
case Giga:
return "G";
case Terra:
return "T";
case Peta:
return "P";
}
return "?";
}
const char* toString(Unit::Name unit) {
using enum Unit::Name;
switch (unit) {
case None:
return "";
case BitsPerSecond:
return "bps";
case BytesPerSecond:
return "Bps";
case Herz:
return "Hz";
case Decibel:
return "dB";
case DecibelMilliwatts:
return "dBm";
}
return "?";
}
}