2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2019-11-14 22:51:33 -08:00
|
|
|
|
|
|
|
|
#include "frc/DutyCycleEncoder.h"
|
|
|
|
|
|
2024-09-20 17:43:39 -07:00
|
|
|
#include <memory>
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
2021-05-26 07:24:53 -07:00
|
|
|
#include <wpi/NullDeleter.h>
|
2021-06-13 16:38:05 -07:00
|
|
|
#include <wpi/sendable/SendableBuilder.h>
|
2021-05-26 07:24:53 -07:00
|
|
|
|
2019-12-06 20:58:04 -08:00
|
|
|
#include "frc/DigitalInput.h"
|
2019-11-14 22:51:33 -08:00
|
|
|
#include "frc/DigitalSource.h"
|
|
|
|
|
#include "frc/DutyCycle.h"
|
2024-05-24 11:53:56 -07:00
|
|
|
#include "frc/MathUtil.h"
|
2019-11-14 22:51:33 -08:00
|
|
|
|
|
|
|
|
using namespace frc;
|
|
|
|
|
|
2019-12-06 20:58:04 -08:00
|
|
|
DutyCycleEncoder::DutyCycleEncoder(int channel)
|
2025-01-15 11:57:31 -08:00
|
|
|
: m_dutyCycle{std::make_shared<DutyCycle>(channel)} {
|
2024-05-24 11:53:56 -07:00
|
|
|
Init(1.0, 0.0);
|
2019-12-09 21:35:00 -08:00
|
|
|
}
|
2019-12-06 20:58:04 -08:00
|
|
|
|
2019-11-14 22:51:33 -08:00
|
|
|
DutyCycleEncoder::DutyCycleEncoder(DutyCycle& dutyCycle)
|
2021-05-26 07:24:53 -07:00
|
|
|
: m_dutyCycle{&dutyCycle, wpi::NullDeleter<DutyCycle>{}} {
|
2024-05-24 11:53:56 -07:00
|
|
|
Init(1.0, 0.0);
|
2019-11-14 22:51:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DutyCycleEncoder::DutyCycleEncoder(DutyCycle* dutyCycle)
|
2021-05-26 07:24:53 -07:00
|
|
|
: m_dutyCycle{dutyCycle, wpi::NullDeleter<DutyCycle>{}} {
|
2024-05-24 11:53:56 -07:00
|
|
|
Init(1.0, 0.0);
|
2019-11-14 22:51:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DutyCycleEncoder::DutyCycleEncoder(std::shared_ptr<DutyCycle> dutyCycle)
|
2020-03-20 16:32:52 -05:00
|
|
|
: m_dutyCycle{std::move(dutyCycle)} {
|
2024-05-24 11:53:56 -07:00
|
|
|
Init(1.0, 0.0);
|
2019-11-14 22:51:33 -08:00
|
|
|
}
|
|
|
|
|
|
2024-05-24 11:53:56 -07:00
|
|
|
DutyCycleEncoder::DutyCycleEncoder(int channel, double fullRange,
|
|
|
|
|
double expectedZero)
|
2025-01-15 11:57:31 -08:00
|
|
|
: m_dutyCycle{std::make_shared<DutyCycle>(channel)} {
|
2024-05-24 11:53:56 -07:00
|
|
|
Init(fullRange, expectedZero);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DutyCycleEncoder::DutyCycleEncoder(DutyCycle& dutyCycle, double fullRange,
|
|
|
|
|
double expectedZero)
|
|
|
|
|
: m_dutyCycle{&dutyCycle, wpi::NullDeleter<DutyCycle>{}} {
|
|
|
|
|
Init(fullRange, expectedZero);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DutyCycleEncoder::DutyCycleEncoder(DutyCycle* dutyCycle, double fullRange,
|
|
|
|
|
double expectedZero)
|
|
|
|
|
: m_dutyCycle{dutyCycle, wpi::NullDeleter<DutyCycle>{}} {
|
|
|
|
|
Init(fullRange, expectedZero);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DutyCycleEncoder::DutyCycleEncoder(std::shared_ptr<DutyCycle> dutyCycle,
|
|
|
|
|
double fullRange, double expectedZero)
|
|
|
|
|
: m_dutyCycle{std::move(dutyCycle)} {
|
|
|
|
|
Init(fullRange, expectedZero);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DutyCycleEncoder::Init(double fullRange, double expectedZero) {
|
2020-12-23 15:54:11 -08:00
|
|
|
m_simDevice = hal::SimDevice{"DutyCycle:DutyCycleEncoder",
|
|
|
|
|
m_dutyCycle->GetSourceChannel()};
|
2019-11-14 22:51:33 -08:00
|
|
|
|
|
|
|
|
if (m_simDevice) {
|
2024-05-24 11:53:56 -07:00
|
|
|
m_simPosition = m_simDevice.CreateDouble("Position", false, 0.0);
|
2020-12-23 15:54:11 -08:00
|
|
|
m_simIsConnected =
|
2024-05-24 11:53:56 -07:00
|
|
|
m_simDevice.CreateBoolean("Connected", hal::SimDevice::kInput, true);
|
2019-11-14 22:51:33 -08:00
|
|
|
}
|
|
|
|
|
|
2024-05-24 11:53:56 -07:00
|
|
|
m_fullRange = fullRange;
|
|
|
|
|
m_expectedZero = expectedZero;
|
|
|
|
|
|
2021-06-15 23:06:03 -07:00
|
|
|
wpi::SendableRegistry::AddLW(this, "DutyCycle Encoder",
|
|
|
|
|
m_dutyCycle->GetSourceChannel());
|
2019-11-14 22:51:33 -08:00
|
|
|
}
|
|
|
|
|
|
2024-05-24 11:53:56 -07:00
|
|
|
double DutyCycleEncoder::Get() const {
|
2020-12-28 12:58:06 -08:00
|
|
|
if (m_simPosition) {
|
2024-05-24 11:53:56 -07:00
|
|
|
return m_simPosition.Get();
|
2020-12-28 12:58:06 -08:00
|
|
|
}
|
2019-11-14 22:51:33 -08:00
|
|
|
|
2024-05-24 11:53:56 -07:00
|
|
|
double pos;
|
|
|
|
|
// Compute output percentage (0-1)
|
|
|
|
|
if (m_period.value() == 0.0) {
|
|
|
|
|
pos = m_dutyCycle->GetOutput();
|
|
|
|
|
} else {
|
|
|
|
|
auto highTime = m_dutyCycle->GetHighTime();
|
|
|
|
|
pos = highTime / m_period;
|
2019-11-14 22:51:33 -08:00
|
|
|
}
|
|
|
|
|
|
2024-05-24 11:53:56 -07:00
|
|
|
// Map sensor range if range isn't full
|
|
|
|
|
pos = MapSensorRange(pos);
|
|
|
|
|
|
|
|
|
|
// Compute full range and offset
|
|
|
|
|
pos = pos * m_fullRange - m_expectedZero;
|
|
|
|
|
|
|
|
|
|
// Map from 0 - Full Range
|
|
|
|
|
double result = InputModulus(pos, 0.0, m_fullRange);
|
|
|
|
|
// Invert if necessary
|
|
|
|
|
if (m_isInverted) {
|
|
|
|
|
return m_fullRange - result;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
2019-11-14 22:51:33 -08:00
|
|
|
}
|
|
|
|
|
|
2022-02-24 22:45:15 -08:00
|
|
|
double DutyCycleEncoder::MapSensorRange(double pos) const {
|
|
|
|
|
if (pos < m_sensorMin) {
|
|
|
|
|
pos = m_sensorMin;
|
|
|
|
|
}
|
|
|
|
|
if (pos > m_sensorMax) {
|
|
|
|
|
pos = m_sensorMax;
|
|
|
|
|
}
|
|
|
|
|
pos = (pos - m_sensorMin) / (m_sensorMax - m_sensorMin);
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-05 14:28:08 -08:00
|
|
|
void DutyCycleEncoder::SetDutyCycleRange(double min, double max) {
|
|
|
|
|
m_sensorMin = std::clamp(min, 0.0, 1.0);
|
|
|
|
|
m_sensorMax = std::clamp(max, 0.0, 1.0);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-14 22:51:33 -08:00
|
|
|
int DutyCycleEncoder::GetFrequency() const {
|
|
|
|
|
return m_dutyCycle->GetFrequency();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DutyCycleEncoder::IsConnected() const {
|
2020-12-28 12:58:06 -08:00
|
|
|
if (m_simIsConnected) {
|
|
|
|
|
return m_simIsConnected.Get();
|
|
|
|
|
}
|
2019-11-14 22:51:33 -08:00
|
|
|
return GetFrequency() > m_frequencyThreshold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DutyCycleEncoder::SetConnectedFrequencyThreshold(int frequency) {
|
|
|
|
|
if (frequency < 0) {
|
|
|
|
|
frequency = 0;
|
|
|
|
|
}
|
|
|
|
|
m_frequencyThreshold = frequency;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-24 11:53:56 -07:00
|
|
|
void DutyCycleEncoder::SetInverted(bool inverted) {
|
|
|
|
|
m_isInverted = inverted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DutyCycleEncoder::SetAssumedFrequency(units::hertz_t frequency) {
|
|
|
|
|
if (frequency.value() == 0) {
|
|
|
|
|
m_period = 0_s;
|
|
|
|
|
} else {
|
|
|
|
|
m_period = 1.0 / frequency;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-24 20:18:58 -07:00
|
|
|
int DutyCycleEncoder::GetFPGAIndex() const {
|
|
|
|
|
return m_dutyCycle->GetFPGAIndex();
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-23 20:18:49 -07:00
|
|
|
int DutyCycleEncoder::GetSourceChannel() const {
|
|
|
|
|
return m_dutyCycle->GetSourceChannel();
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-13 16:38:05 -07:00
|
|
|
void DutyCycleEncoder::InitSendable(wpi::SendableBuilder& builder) {
|
2019-11-14 22:51:33 -08:00
|
|
|
builder.SetSmartDashboardType("AbsoluteEncoder");
|
2020-06-27 20:39:00 -07:00
|
|
|
builder.AddDoubleProperty(
|
2024-05-24 11:53:56 -07:00
|
|
|
"Position", [this] { return this->Get(); }, nullptr);
|
2020-06-27 20:39:00 -07:00
|
|
|
builder.AddDoubleProperty(
|
|
|
|
|
"Is Connected", [this] { return this->IsConnected(); }, nullptr);
|
2019-11-14 22:51:33 -08:00
|
|
|
}
|