Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/v2/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ namespace pocketpd {
/**
* @brief Latest sensor reading from INA226.
*/
struct SensorSnapshot {
struct LoadReading {
uint32_t timestamp_ms = 0;
uint32_t vbus_mv = 0;
uint32_t current_ma = 0;
Expand All @@ -77,7 +77,7 @@ namespace pocketpd {
* @brief Published by SensorTask. Carries one bus reading.
*/
struct SensorEvent {
SensorSnapshot snapshot;
LoadReading load;
};

/**
Expand Down
9 changes: 5 additions & 4 deletions include/v2/stages/energy/energy_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace pocketpd {
* @brief Frozen snapshot of everything EnergyView needs to draw one frame.
*/
struct EnergyViewModel {
SensorSnapshot snapshot{};
LoadReading load_reading{};
double accumulated_wh = 0.0;
double accumulated_ah = 0.0;
uint32_t total_seconds = 0;
Expand Down Expand Up @@ -72,10 +72,10 @@ namespace pocketpd {
display.set_font(tempo::Font::BASE);

// Mid — Voltage Current
format_milli(buf, vm.snapshot.vbus_mv, 'V');
format_milli(buf, vm.load_reading.vbus_mv, 'V');
display.draw_text(V_X, ROW2_Y - 10, buf.data());

format_milli(buf, vm.snapshot.current_ma, 'A');
format_milli(buf, vm.load_reading.current_ma, 'A');
display.draw_text(A_X, ROW2_Y + 5, buf.data());

// Bottom row — Wh Time Ah
Expand All @@ -90,7 +90,8 @@ namespace pocketpd {

// The big W label in the middle
display.set_font(tempo::Font::XL);
const double watts = (vm.snapshot.vbus_mv * vm.snapshot.current_ma) / 1'000'000.0;
const double watts =
(vm.load_reading.vbus_mv * vm.load_reading.current_ma) / 1'000'000.0;
format_auto(buf, watts);
draw_value(display, COL1_X, ROW2_Y, buf.data(), "W");

Expand Down
30 changes: 10 additions & 20 deletions include/v2/stages/energy_stage.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "v2/images.h"
#include "v2/stages/energy/energy_view.h"
#include "v2/pocketpd.h"
#include "v2/util/filter.h"

namespace pocketpd {

Expand All @@ -32,8 +33,8 @@ namespace pocketpd {

Display& m_display;
OutputGate& m_output_gate;
SensorSnapshot m_snapshot{};
bool m_snapshot_seeded = false;
LoadReading m_load_reading{};
bool m_load_init = false;
EnergyEvent m_energy{};
int8_t m_active_pdo_index = -1;
tempo::IntervalTimer m_render_interval{40};
Expand Down Expand Up @@ -96,7 +97,12 @@ namespace pocketpd {
conductor.request<NormalStage>(m_active_pdo_index);
}
},
[&](const SensorEvent& evt) { ema_filter(evt.snapshot); },
[&](const SensorEvent& evt) {
m_load_reading = m_load_init
? Filter::ema(m_load_reading, evt.load, SENSOR_EMA_DEN)
: evt.load;
m_load_init = true;
},
[&](const EnergyEvent& evt) { m_energy = evt; },
[](const auto&) {},
};
Expand All @@ -105,25 +111,9 @@ namespace pocketpd {
}

private:
/**
* @brief Apply EMA smoothing to displayed mV / mA. Same shape as
* NormalStage so the readout is consistent across screens.
*/
void ema_filter(const SensorSnapshot& s) {
if (!m_snapshot_seeded) {
m_snapshot = s;
m_snapshot_seeded = true;
return;
}
const uint32_t a = SENSOR_EMA_DEN - 1;
m_snapshot.vbus_mv = (m_snapshot.vbus_mv * a + s.vbus_mv) / SENSOR_EMA_DEN;
m_snapshot.current_ma = (m_snapshot.current_ma * a + s.current_ma) / SENSOR_EMA_DEN;
m_snapshot.timestamp_ms = s.timestamp_ms;
}

EnergyViewModel build_view_model() const {
return EnergyViewModel{
.snapshot = m_snapshot,
.load_reading = m_load_reading,
.accumulated_wh = m_energy.accumulated_wh,
.accumulated_ah = m_energy.accumulated_ah,
.total_seconds = m_energy.total_seconds,
Expand Down
10 changes: 7 additions & 3 deletions include/v2/stages/normal/normal_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace pocketpd {
bool readout_visible = true;
bool locked = false;
uint8_t arrow_frame = 0;
SensorSnapshot snapshot{};
LoadReading load_reading{};

// —— PPS branch (valid when has_profile && is_pps)

Expand Down Expand Up @@ -79,8 +79,12 @@ namespace pocketpd {
std::array<char, 32> buf{};

d.set_font(tempo::Font::XL);
draw_measured(d, "V", V_MEASURED_Y, vm.snapshot.vbus_mv, buf, vm.readout_visible);
draw_measured(d, "A", A_MEASURED_Y, vm.snapshot.current_ma, buf, vm.readout_visible);
draw_measured(
d, "V", V_MEASURED_Y, vm.load_reading.vbus_mv, buf, vm.readout_visible
);
draw_measured(
d, "A", A_MEASURED_Y, vm.load_reading.current_ma, buf, vm.readout_visible
);

d.set_font(tempo::Font::BASE);
std::snprintf(buf.data(), buf.size(), "[%u]", vm.active_pdo_index);
Expand Down
32 changes: 10 additions & 22 deletions include/v2/stages/normal_stage.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "v2/stages/normal/fixed_mode.h"
#include "v2/stages/normal/normal_view.h"
#include "v2/stages/normal/pps_mode.h"
#include "v2/util/filter.h"

namespace pocketpd {

Expand All @@ -32,8 +33,8 @@ namespace pocketpd {
PdSinkController& m_pd_sink;
OutputGate& m_output_gate;

SensorSnapshot m_snapshot{};
bool m_snapshot_init = false;
LoadReading m_load_reading{};
bool m_load_init = false;

int8_t m_active_pdo_index = -1;
int8_t m_last_active_index = -1;
Expand Down Expand Up @@ -181,32 +182,19 @@ namespace pocketpd {
log.error(msg, m_active_pdo_index, pps->target_mv, pps->target_ma);
}
},
[&](const SensorEvent& evt) { ema_filter(evt.snapshot); },
[&](const SensorEvent& evt) {
m_load_reading = m_load_init
? Filter::ema(m_load_reading, evt.load, SENSOR_EMA_DEN)
: evt.load;
m_load_init = true;
},
[](const auto&) {},
};

std::visit(handler, event);
}

private:
/**
* @brief Apply EMA smoothing to displayed mV / mA.
*/
void ema_filter(const SensorSnapshot& s) {
if (!m_snapshot_init) {
m_snapshot = s;
m_snapshot_init = true;
return;
}

// For SENSOR_EMA_DEN = 4: new = 0.75 * new + 0.25 * old

const uint32_t a = SENSOR_EMA_DEN - 1;
m_snapshot.vbus_mv = (m_snapshot.vbus_mv * a + s.vbus_mv) / SENSOR_EMA_DEN;
m_snapshot.current_ma = (m_snapshot.current_ma * a + s.current_ma) / SENSOR_EMA_DEN;
m_snapshot.timestamp_ms = s.timestamp_ms;
}

/**
* @brief Enter (or re-enter) a PPS profile. Construct fresh state on profile change or
* mode switch; otherwise preserve the user's prior target edits and reissue.
Expand Down Expand Up @@ -252,7 +240,7 @@ namespace pocketpd {
.readout_visible = m_blink_visible,
.locked = m_locked,
.arrow_frame = m_arrow_frame,
.snapshot = m_snapshot,
.load_reading = m_load_reading,
};

if (!vm.has_profile) {
Expand Down
4 changes: 2 additions & 2 deletions include/v2/tasks/energy_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ namespace pocketpd {

void on_event(const Event& event, uint32_t) override {
if (const auto sensor = std::get_if<SensorEvent>(&event)) {
integrate(sensor->snapshot);
integrate(sensor->load);
}
}

Expand All @@ -67,7 +67,7 @@ namespace pocketpd {
}

private:
void integrate(const SensorSnapshot& s) {
void integrate(const LoadReading& s) {
if (!m_output_gate.is_enabled()) {
m_session_active = false;
return;
Expand Down
2 changes: 1 addition & 1 deletion include/v2/tasks/sensor_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace pocketpd {
if (!reading.valid) {
return;
}
publish(SensorEvent{SensorSnapshot{now_ms, reading.mv, reading.ma}});
publish(SensorEvent{LoadReading{now_ms, reading.mv, reading.ma}});
}
};

Expand Down
33 changes: 33 additions & 0 deletions include/v2/util/filter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @file filter.h
* @brief Stateless EMA scalar + snapshot overloads.
*/
#pragma once

#include <cstdint>

#include "v2/events.h"

namespace pocketpd {

class Filter {
public:
/**
* @brief One EMA step. `new = ((den-1)*prev + sample) / den`.
* For `den = 4`: weights 0.75 prev / 0.25 sample.
*/
static uint32_t ema(uint32_t prev, uint32_t sample, uint32_t den) {
return (prev * (den - 1) + sample) / den;
}

/** @brief Per-field EMA across a `LoadReading`. `timestamp_ms` tracks the latest. */
static LoadReading ema(const LoadReading& prev, const LoadReading& sample, uint32_t den) {
return LoadReading{
.timestamp_ms = sample.timestamp_ms,
.vbus_mv = ema(prev.vbus_mv, sample.vbus_mv, den),
.current_ma = ema(prev.current_ma, sample.current_ma, den),
};
}
};

} // namespace pocketpd
2 changes: 1 addition & 1 deletion test/test_v2_energy/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ using TestConductor = App::Conductor;
namespace {

SensorEvent make_sensor(uint32_t ts_ms, uint32_t mv, uint32_t ma) {
return SensorEvent{SensorSnapshot{ts_ms, mv, ma}};
return SensorEvent{LoadReading{ts_ms, mv, ma}};
}

const EnergyEvent* drain_last_energy(TestQueue& q) {
Expand Down
4 changes: 2 additions & 2 deletions test/test_v2_normal/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ TEST(NormalStage, OnEnterPdoBranchRendersVAReadoutAndPdoIndex) {
TestConductor conductor;
conductor.register_stage(normal);
normal.prepare(2);
normal.on_event(conductor, SensorEvent{SensorSnapshot{0, 5000, 1234}}, 0);
normal.on_event(conductor, SensorEvent{LoadReading{0, 5000, 1234}}, 0);
conductor.start<NormalStage>();
}

Expand Down Expand Up @@ -420,7 +420,7 @@ TEST(NormalView, ReadoutHiddenKeepsLabelsHidesValues) {
vm.output_enabled = false;
vm.readout_visible = false;
vm.active_pdo_index = 0;
vm.snapshot = SensorSnapshot{0, 5000, 1234};
vm.load_reading = LoadReading{0, 5000, 1234};

NormalView::render(display, vm);
}
Expand Down
6 changes: 3 additions & 3 deletions test/test_v2_sensor/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ TEST(SensorTask, OnTickPublishesSensorEventWithTimestampAndValues) {

const auto* evt = pop_sensor(q);
ASSERT_NE(evt, nullptr);
EXPECT_EQ(evt->snapshot.timestamp_ms, 42u);
EXPECT_EQ(evt->snapshot.vbus_mv, 5000u);
EXPECT_EQ(evt->snapshot.current_ma, 1234u);
EXPECT_EQ(evt->load.timestamp_ms, 42u);
EXPECT_EQ(evt->load.vbus_mv, 5000u);
EXPECT_EQ(evt->load.current_ma, 1234u);
}

TEST(SensorTask, InvalidReadingDoesNotPublish) {
Expand Down
Loading