blob: f04af21059ceb266e6592cbdf56acb20cc9d0659 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/media/audio/lib/clock/pid_control.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/time.h>
#include <optional>
#include <string>
#include "src/lib/fxl/strings/concatenate.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace media::audio::clock {
// Reset the PID controller
void PidControl::Reset() {
*this = PidControl({
.proportional_factor = proportional_factor_,
.integral_factor = integral_factor_,
.derivative_factor = derivative_factor_,
});
}
// Set the start time and reset the PID controller
void PidControl::Start(zx::time start_time) {
Reset();
tune_time_ = start_time;
}
double PidControl::Read() const { return total_pid_contribution_; }
// Factor in the most current error reading. If at/before the previous time, ignore it.
void PidControl::TuneForError(zx::time time_of_error, double error) {
if (!tune_time_.has_value()) {
FX_LOGS(INFO) << "TuneForError before Start: setting tune_time_(" << time_of_error.get()
<< ") and current_error_(" << error << ")";
tune_time_ = time_of_error;
current_error_ = error;
return;
}
if (time_of_error <= *tune_time_) {
FX_LOGS_FIRST_N(WARNING, 100) << __func__ << " ignored, time (" << time_of_error.get()
<< ") should exceed previous update (" << tune_time_->get()
<< ")";
return;
}
auto duration = static_cast<double>((time_of_error - *tune_time_).get());
tune_time_ = time_of_error;
delta_error_ = (error - current_error_) / duration;
accum_error_ += (error * duration);
current_error_ = error;
// TODO(https://fxbug.dev/42164004): if needed, add low-pass filtering to the Derivative contributions
prop_contrib_ = current_error_ * proportional_factor_;
integ_contrib_ = accum_error_ * integral_factor_;
deriv_contrib_ = delta_error_ * derivative_factor_;
total_pid_contribution_ = prop_contrib_ + integ_contrib_ + deriv_contrib_;
}
std::ostream& operator<<(std::ostream& out, const PidControl& pid) {
out << &pid << "; Factors "
<< fxl::StringPrintf("%8.1e|%8.1e|%8.1e", pid.proportional_factor_, pid.integral_factor_,
pid.derivative_factor_)
<< "; Errors "
<< fxl::StringPrintf("%8.1e|%8.1e|%8.1e", pid.current_error_, pid.accum_error_,
pid.delta_error_)
<< "; Contribs "
<< fxl::StringPrintf("%8.1e|%8.1e|%8.1e=%10.2e", pid.prop_contrib_, pid.integ_contrib_,
pid.deriv_contrib_, pid.total_pid_contribution_)
<< "; tune_time ";
if (pid.tune_time_) {
out << pid.tune_time_->get();
} else {
out << "UNSET";
}
return out;
}
} // namespace media::audio::clock