blob: 0c29bea5e57cb93d3efd834a66d0c99a424d9e36 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "frc/AnalogEncoder.h"
6
Austin Schuh812d0d12021-11-04 20:16:48 -07007#include <wpi/NullDeleter.h>
8#include <wpi/sendable/SendableBuilder.h>
9
Brian Silverman8fce7482020-01-05 13:18:21 -080010#include "frc/AnalogInput.h"
11#include "frc/Counter.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070012#include "frc/Errors.h"
James Kuszmaulcf324122023-01-14 14:07:17 -080013#include "frc/RobotController.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080014
15using namespace frc;
16
Austin Schuh812d0d12021-11-04 20:16:48 -070017AnalogEncoder::AnalogEncoder(int channel)
18 : AnalogEncoder(std::make_shared<AnalogInput>(channel)) {}
19
Brian Silverman8fce7482020-01-05 13:18:21 -080020AnalogEncoder::AnalogEncoder(AnalogInput& analogInput)
Austin Schuh812d0d12021-11-04 20:16:48 -070021 : m_analogInput{&analogInput, wpi::NullDeleter<AnalogInput>{}},
Brian Silverman8fce7482020-01-05 13:18:21 -080022 m_analogTrigger{m_analogInput.get()},
23 m_counter{} {
24 Init();
25}
26
27AnalogEncoder::AnalogEncoder(AnalogInput* analogInput)
Austin Schuh812d0d12021-11-04 20:16:48 -070028 : m_analogInput{analogInput, wpi::NullDeleter<AnalogInput>{}},
Brian Silverman8fce7482020-01-05 13:18:21 -080029 m_analogTrigger{m_analogInput.get()},
30 m_counter{} {
31 Init();
32}
33
34AnalogEncoder::AnalogEncoder(std::shared_ptr<AnalogInput> analogInput)
35 : m_analogInput{std::move(analogInput)},
36 m_analogTrigger{m_analogInput.get()},
37 m_counter{} {
38 Init();
39}
40
41void AnalogEncoder::Init() {
42 m_simDevice = hal::SimDevice{"AnalogEncoder", m_analogInput->GetChannel()};
43
44 if (m_simDevice) {
45 m_simPosition = m_simDevice.CreateDouble("Position", false, 0.0);
James Kuszmaulcf324122023-01-14 14:07:17 -080046 m_simAbsolutePosition =
47 m_simDevice.CreateDouble("absPosition", hal::SimDevice::kInput, 0.0);
Brian Silverman8fce7482020-01-05 13:18:21 -080048 }
49
50 m_analogTrigger.SetLimitsVoltage(1.25, 3.75);
51 m_counter.SetUpSource(
52 m_analogTrigger.CreateOutput(AnalogTriggerType::kRisingPulse));
53 m_counter.SetDownSource(
54 m_analogTrigger.CreateOutput(AnalogTriggerType::kFallingPulse));
55
Austin Schuh812d0d12021-11-04 20:16:48 -070056 wpi::SendableRegistry::AddLW(this, "DutyCycle Encoder",
57 m_analogInput->GetChannel());
Brian Silverman8fce7482020-01-05 13:18:21 -080058}
59
James Kuszmaulcf324122023-01-14 14:07:17 -080060static bool DoubleEquals(double a, double b) {
61 constexpr double epsilon = 0.00001;
62 return std::abs(a - b) < epsilon;
63}
64
Brian Silverman8fce7482020-01-05 13:18:21 -080065units::turn_t AnalogEncoder::Get() const {
Austin Schuh812d0d12021-11-04 20:16:48 -070066 if (m_simPosition) {
67 return units::turn_t{m_simPosition.Get()};
68 }
Brian Silverman8fce7482020-01-05 13:18:21 -080069
70 // As the values are not atomic, keep trying until we get 2 reads of the same
71 // value If we don't within 10 attempts, error
72 for (int i = 0; i < 10; i++) {
73 auto counter = m_counter.Get();
74 auto pos = m_analogInput->GetVoltage();
75 auto counter2 = m_counter.Get();
76 auto pos2 = m_analogInput->GetVoltage();
James Kuszmaulcf324122023-01-14 14:07:17 -080077 if (counter == counter2 && DoubleEquals(pos, pos2)) {
78 pos = pos / frc::RobotController::GetVoltage5V();
Brian Silverman8fce7482020-01-05 13:18:21 -080079 units::turn_t turns{counter + pos - m_positionOffset};
80 m_lastPosition = turns;
81 return turns;
82 }
83 }
84
Austin Schuh812d0d12021-11-04 20:16:48 -070085 FRC_ReportError(
James Kuszmaulcf324122023-01-14 14:07:17 -080086 warn::Warning,
Brian Silverman8fce7482020-01-05 13:18:21 -080087 "Failed to read Analog Encoder. Potential Speed Overrun. Returning last "
88 "value");
89 return m_lastPosition;
90}
91
James Kuszmaulcf324122023-01-14 14:07:17 -080092double AnalogEncoder::GetAbsolutePosition() const {
93 if (m_simAbsolutePosition) {
94 return m_simAbsolutePosition.Get();
95 }
96
97 return m_analogInput->GetVoltage() / frc::RobotController::GetVoltage5V();
98}
99
Austin Schuh812d0d12021-11-04 20:16:48 -0700100double AnalogEncoder::GetPositionOffset() const {
101 return m_positionOffset;
102}
Brian Silverman8fce7482020-01-05 13:18:21 -0800103
James Kuszmaulcf324122023-01-14 14:07:17 -0800104void AnalogEncoder::SetPositionOffset(double offset) {
105 m_positionOffset = std::clamp(offset, 0.0, 1.0);
106}
107
Brian Silverman8fce7482020-01-05 13:18:21 -0800108void AnalogEncoder::SetDistancePerRotation(double distancePerRotation) {
109 m_distancePerRotation = distancePerRotation;
110}
111
112double AnalogEncoder::GetDistancePerRotation() const {
113 return m_distancePerRotation;
114}
115
116double AnalogEncoder::GetDistance() const {
Austin Schuh812d0d12021-11-04 20:16:48 -0700117 return Get().value() * GetDistancePerRotation();
Brian Silverman8fce7482020-01-05 13:18:21 -0800118}
119
120void AnalogEncoder::Reset() {
121 m_counter.Reset();
James Kuszmaulcf324122023-01-14 14:07:17 -0800122 m_positionOffset =
123 m_analogInput->GetVoltage() / frc::RobotController::GetVoltage5V();
Brian Silverman8fce7482020-01-05 13:18:21 -0800124}
125
Austin Schuh812d0d12021-11-04 20:16:48 -0700126int AnalogEncoder::GetChannel() const {
127 return m_analogInput->GetChannel();
128}
Austin Schuh1e69f942020-11-14 15:06:14 -0800129
Austin Schuh812d0d12021-11-04 20:16:48 -0700130void AnalogEncoder::InitSendable(wpi::SendableBuilder& builder) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800131 builder.SetSmartDashboardType("AbsoluteEncoder");
Austin Schuh1e69f942020-11-14 15:06:14 -0800132 builder.AddDoubleProperty(
133 "Distance", [this] { return this->GetDistance(); }, nullptr);
134 builder.AddDoubleProperty(
135 "Distance Per Rotation",
136 [this] { return this->GetDistancePerRotation(); }, nullptr);
Brian Silverman8fce7482020-01-05 13:18:21 -0800137}