mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-09-21 20:39:35 +00:00
feat(protocol): derive wire size and airtime at compile time
This commit is contained in:
50
tests/arduino_stubs/Arduino.h
Normal file
50
tests/arduino_stubs/Arduino.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
struct GPIO_TypeDef
|
||||
{
|
||||
uint32_t BSRR = 0U;
|
||||
uint32_t IDR = 0U;
|
||||
};
|
||||
|
||||
using IRQn_Type = int;
|
||||
enum TimerFormat_t : uint8_t { TICK_FORMAT = 0, MICROSEC_FORMAT, HERTZ_FORMAT };
|
||||
|
||||
constexpr uint8_t LOW = 0U;
|
||||
constexpr uint8_t HIGH = 1U;
|
||||
constexpr uint8_t INPUT = 0U;
|
||||
constexpr uint8_t OUTPUT = 1U;
|
||||
|
||||
class HardwareTimer
|
||||
{
|
||||
public:
|
||||
void pause() {}
|
||||
void resume() {}
|
||||
void setOverflow(uint32_t value, TimerFormat_t = TICK_FORMAT) { overflow_ = value; }
|
||||
uint32_t getOverflow(TimerFormat_t = TICK_FORMAT) { return overflow_; }
|
||||
uint32_t getPrescaleFactor() { return 1U; }
|
||||
uint32_t getTimerClkFreq() { return 12000000U; }
|
||||
void attachInterrupt(uint8_t, void (*)()) {}
|
||||
|
||||
private:
|
||||
uint32_t overflow_ = 1U;
|
||||
};
|
||||
|
||||
inline GPIO_TypeDef *digitalPinToPort(uint8_t) { return nullptr; }
|
||||
inline uint16_t digitalPinToBitMask(uint8_t) { return 0U; }
|
||||
inline void pinMode(uint8_t, uint8_t) {}
|
||||
inline void digitalWrite(uint8_t, uint8_t) {}
|
||||
inline void NVIC_SetPriority(IRQn_Type, uint8_t) {}
|
||||
inline void noInterrupts() {}
|
||||
inline void interrupts() {}
|
||||
|
||||
struct ArduinoSerialStub
|
||||
{
|
||||
template <typename T> void print(const T &) {}
|
||||
template <typename T> void println(const T &) {}
|
||||
void println() {}
|
||||
};
|
||||
|
||||
inline ArduinoSerialStub Serial;
|
||||
85
tests/test_timing_contract.cpp
Normal file
85
tests/test_timing_contract.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
#include "IR_Encoder.h"
|
||||
#include "IR_DecoderRaw.h"
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
// Link seams: these paths are not exercised by the pure host timing test.
|
||||
bool IR_DecoderRaw::registerPairMuteEncoder(IR_Encoder *) { return true; }
|
||||
void IR_DecoderRaw::refreshPairMuteState() {}
|
||||
void IR_Encoder::send_HIGH(bool) {}
|
||||
void IR_Encoder::send_LOW() {}
|
||||
void IR_Encoder::send_EMPTY(uint8_t) {}
|
||||
|
||||
namespace
|
||||
{
|
||||
static_assert(irproto::dataWireBytes(0U) == 7U, "empty DATA wire size changed");
|
||||
static_assert(irproto::dataWireBytes(3U) == 10U, "DATA wire size changed");
|
||||
static_assert(irproto::dataWireBytes(24U) == 31U, "maximum DATA wire size changed");
|
||||
static_assert(irproto::dataWireBytes(25U) == 0U, "oversized DATA must be rejected");
|
||||
static_assert(irproto::backWireBytes(1U) == 6U, "BACK wire size changed");
|
||||
static_assert(irproto::backWireBytes(26U) == 31U, "maximum BACK wire size changed");
|
||||
static_assert(irproto::backToWireBytes(24U) == 31U, "maximum BACK_TO wire size changed");
|
||||
|
||||
static_assert(irproto::wireLogicalTicks(6U) == 5472U, "6-byte tick count changed");
|
||||
static_assert(irproto::wireLogicalTicks(10U) == 8728U, "10-byte tick count changed");
|
||||
static_assert(irproto::wireLogicalTicks(31U) == 25822U, "31-byte tick count changed");
|
||||
static_assert(irproto::preambleAirtimeUsCeil() == 7737U, "preamble airtime changed");
|
||||
static_assert(irproto::wireAirtimeUsCeil(6U) == 72000U, "6-byte airtime changed");
|
||||
static_assert(irproto::wireAirtimeUsCeil(10U) == 114843U, "10-byte airtime changed");
|
||||
static_assert(irproto::wireAirtimeUsCeil(31U) == 339764U, "31-byte airtime changed");
|
||||
static_assert(irproto::responseStartGuardUs() == 57198U, "response-start guard changed");
|
||||
static_assert(irproto::responseFrameGuardUs(6U) == 131100U, "response-frame guard changed");
|
||||
|
||||
uint32_t sumLogicalTicks(const IrTxGateRun *runs, size_t count)
|
||||
{
|
||||
uint32_t total = 0U;
|
||||
for (size_t i = 0U; i < count; ++i)
|
||||
total += runs[i].lenTicks;
|
||||
return total;
|
||||
}
|
||||
|
||||
void verifyFormulaAgainstTxFsm()
|
||||
{
|
||||
std::array<uint8_t, irproto::kMaxWireFrameBytes> frame{};
|
||||
std::array<IrTxGateRun, 1024U> runs{};
|
||||
|
||||
for (uint8_t wireBytes = 1U; wireBytes <= irproto::kMaxWireFrameBytes; ++wireBytes)
|
||||
{
|
||||
for (uint8_t pattern = 0U; pattern < 4U; ++pattern)
|
||||
{
|
||||
for (uint8_t i = 0U; i < wireBytes; ++i)
|
||||
{
|
||||
frame[i] = pattern == 0U ? 0x00U
|
||||
: pattern == 1U ? 0xFFU
|
||||
: pattern == 2U ? static_cast<uint8_t>((i & 1U) ? 0x55U : 0xAAU)
|
||||
: static_cast<uint8_t>(i * 73U + 19U);
|
||||
}
|
||||
const size_t count = IR_Encoder::buildGateRuns(
|
||||
frame.data(), wireBytes, runs.data(), runs.size());
|
||||
assert(count != 0U);
|
||||
assert(sumLogicalTicks(runs.data(), count) == irproto::wireLogicalTicks(wireBytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void verifyPublicSendTimeResults()
|
||||
{
|
||||
IR_Encoder encoder(1U, 42U, nullptr, false);
|
||||
uint8_t payload[26]{};
|
||||
|
||||
assert(encoder.testSendAccept(1U) == 72U); // six-byte wire frame
|
||||
assert(encoder.testSendTime(1U, payload, 3U) == 115U); // ten-byte wire frame
|
||||
assert(encoder.testSendBack(payload, 26U) == 340U); // 31-byte wire frame
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
verifyFormulaAgainstTxFsm();
|
||||
verifyPublicSendTimeResults();
|
||||
std::cout << "IR timing contract tests: OK\n";
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user