Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Fix bug in frequency calculator that will reset the frequency to 0.0 even though data is still incoming. #1650

Merged
merged 2 commits into from
Jul 10, 2024
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
46 changes: 31 additions & 15 deletions ecal/core/src/util/frequency_calculator.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* ========================= eCAL LICENSE =================================
/* ========================= eCAL LICENSE =================================
*
* Copyright (C) 2016 - 2019 Continental Corporation
* Copyright (C) 2016 - 2024 Continental Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -52,16 +52,16 @@ namespace eCAL

FrequencyCalculator(const time_point& now_)
: counted_elements(0)
, first_tick(now_)
, last_tick(now_)
, first_tick_time(now_)
, last_tick_time(now_)
, previous_frequency(0.0)
{
}

void addTick(const time_point& now)
{
counted_elements++;
last_tick = now;
last_tick_time = now;
}

double getFrequency()
Expand All @@ -71,16 +71,16 @@ namespace eCAL
return previous_frequency;
}

previous_frequency = calculateFrequency<T>(first_tick, last_tick, counted_elements);
first_tick = last_tick;
previous_frequency = calculateFrequency<T>(first_tick_time, last_tick_time, counted_elements);
first_tick_time = last_tick_time;
counted_elements = 0;
return previous_frequency;
}

private:
long long counted_elements;
time_point first_tick;
time_point last_tick;
time_point first_tick_time;
time_point last_tick_time;
double previous_frequency;
};

Expand All @@ -105,22 +105,37 @@ namespace eCAL
{
calculator->addTick(now);
}
last_tick = now;
last_tick_time = now;
received_tick_since_get_frequency_called = true;
}

double getFrequency(const time_point& now)
{
double frequency = calculator ? calculator->getFrequency() : 0.0;

// if the frequency is 0.0, return it right away
if (frequency == 0.0)
{
return 0.0;
}

// calculate theoretical frequency to detect timeouts;
double theoretical_frequency = calculateFrequency<T>(last_tick, now, 1);
// If the frequency is higher than reset_factor * theoretical_frequency, we reset.
if (frequency >= theoretical_frequency * reset_factor)
// if we have received ticks, this means we don't want to reset the calculation
// so we return the frequency that the calculator has calculated
if (received_tick_since_get_frequency_called)
{
received_tick_since_get_frequency_called = false;
return frequency;
}

// if we have not received ticks since the last call of getFrequency
// then we calculate the ΔT, e.g. the timespan in seconds between last received and next incoming element
// Based on the last time we received a tick we could calculate when we expect the next tick
// We can also calculate when we want to time out, based on the reset factor
// timeout = last_tick + ΔT * reset_factor
// If the current time is greater than the timeout time, we reset the calculator and return 0
auto expected_time_in_seconds_between_last_and_next_tick = std::chrono::duration<double>(1 / frequency);
time_point timeout_time = last_tick_time + std::chrono::duration_cast<std::chrono::nanoseconds>(expected_time_in_seconds_between_last_and_next_tick * reset_factor);
if (now > timeout_time)
{
calculator.reset();
return 0.0;
Expand All @@ -131,7 +146,8 @@ namespace eCAL

private:
float reset_factor;
time_point last_tick;
time_point last_tick_time;
bool received_tick_since_get_frequency_called = false;
std::unique_ptr<FrequencyCalculator<T>> calculator;
};
}
Expand Down
55 changes: 54 additions & 1 deletion ecal/tests/cpp/util_test/src/util_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* ========================= eCAL LICENSE =================================
*
* Copyright (C) 2016 - 2019 Continental Corporation
* Copyright (C) 2016 - 2024 Continental Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -194,3 +194,56 @@ TEST(core_cpp_util, Freq_ResettableFrequencyCalculator)
}
}
}


struct command
{
std::string command_type;
long long time_point;
};

std::vector<command> commands =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'commands' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables]

std::vector<command> commands =
                     ^

{
command{"getting frequency", 0 },
command{"ticking", 500 },
command{"getting frequency", 1000 },
command{"ticking", 1500 },
command{"getting frequency", 2000 },
command{"ticking", 2500 },
command{"getting frequency", 3000 },
command{"ticking", 3998 },
command{"getting frequency", 4000 },
command{"ticking", 4002 },
command{"getting frequency", 5000 },
command{"ticking", 5998 },
command{"getting frequency", 6000 },
};

TEST(core_cpp_util, Freq_NoZeroFrequency)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: all parameters should be named in a function [readability-named-parameter]

Suggested change
TEST(core_cpp_util, Freq_NoZeroFrequency)
TEST(core_cpp_util /*unused*/, Freq_NoZeroFrequency /*unused*/)

{
eCAL::ResettableFrequencyCalculator<std::chrono::steady_clock> calculator(3.0f);
int i = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'i' of type 'int' can be declared 'const' [misc-const-correctness]

Suggested change
int i = 0;
int const i = 0;


for (const auto& command : commands)
{
std::chrono::steady_clock::time_point time(std::chrono::milliseconds(command.time_point));

if (command.command_type == "ticking")
{
calculator.addTick(time);
}
else if (command.command_type == "getting frequency")
{
auto frequency = calculator.getFrequency(time);
if (i > 1)
{
ASSERT_GT(frequency, 0.0) << "Iteration " << i;
}

++i;
}
else
{
}
}
}
Loading