blob: b1c943ed7b4c4d2d891f43815f8b3930a4847cbe [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/DutyCycleEncoder.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/Counter.h"
11#include "frc/DigitalInput.h"
12#include "frc/DigitalSource.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080013#include "frc/DutyCycle.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070014#include "frc/Errors.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080015
16using namespace frc;
17
18DutyCycleEncoder::DutyCycleEncoder(int channel)
19 : m_dutyCycle{std::make_shared<DutyCycle>(
Austin Schuh1e69f942020-11-14 15:06:14 -080020 std::make_shared<DigitalInput>(channel))} {
Brian Silverman8fce7482020-01-05 13:18:21 -080021 Init();
22}
23
24DutyCycleEncoder::DutyCycleEncoder(DutyCycle& dutyCycle)
Austin Schuh812d0d12021-11-04 20:16:48 -070025 : m_dutyCycle{&dutyCycle, wpi::NullDeleter<DutyCycle>{}} {
Brian Silverman8fce7482020-01-05 13:18:21 -080026 Init();
27}
28
29DutyCycleEncoder::DutyCycleEncoder(DutyCycle* dutyCycle)
Austin Schuh812d0d12021-11-04 20:16:48 -070030 : m_dutyCycle{dutyCycle, wpi::NullDeleter<DutyCycle>{}} {
Brian Silverman8fce7482020-01-05 13:18:21 -080031 Init();
32}
33
34DutyCycleEncoder::DutyCycleEncoder(std::shared_ptr<DutyCycle> dutyCycle)
Austin Schuh1e69f942020-11-14 15:06:14 -080035 : m_dutyCycle{std::move(dutyCycle)} {
Brian Silverman8fce7482020-01-05 13:18:21 -080036 Init();
37}
38
39DutyCycleEncoder::DutyCycleEncoder(DigitalSource& digitalSource)
Austin Schuh1e69f942020-11-14 15:06:14 -080040 : m_dutyCycle{std::make_shared<DutyCycle>(digitalSource)} {
Brian Silverman8fce7482020-01-05 13:18:21 -080041 Init();
42}
43
44DutyCycleEncoder::DutyCycleEncoder(DigitalSource* digitalSource)
Austin Schuh1e69f942020-11-14 15:06:14 -080045 : m_dutyCycle{std::make_shared<DutyCycle>(digitalSource)} {
Brian Silverman8fce7482020-01-05 13:18:21 -080046 Init();
47}
48
49DutyCycleEncoder::DutyCycleEncoder(std::shared_ptr<DigitalSource> digitalSource)
Austin Schuh1e69f942020-11-14 15:06:14 -080050 : m_dutyCycle{std::make_shared<DutyCycle>(digitalSource)} {
Brian Silverman8fce7482020-01-05 13:18:21 -080051 Init();
52}
53
54void DutyCycleEncoder::Init() {
Austin Schuh812d0d12021-11-04 20:16:48 -070055 m_simDevice = hal::SimDevice{"DutyCycle:DutyCycleEncoder",
56 m_dutyCycle->GetSourceChannel()};
Brian Silverman8fce7482020-01-05 13:18:21 -080057
58 if (m_simDevice) {
Austin Schuh812d0d12021-11-04 20:16:48 -070059 m_simPosition =
60 m_simDevice.CreateDouble("position", hal::SimDevice::kInput, 0.0);
61 m_simDistancePerRotation = m_simDevice.CreateDouble(
62 "distance_per_rot", hal::SimDevice::kOutput, 1.0);
James Kuszmaulcf324122023-01-14 14:07:17 -080063 m_simAbsolutePosition =
64 m_simDevice.CreateDouble("absPosition", hal::SimDevice::kInput, 0.0);
Austin Schuh812d0d12021-11-04 20:16:48 -070065 m_simIsConnected =
66 m_simDevice.CreateBoolean("connected", hal::SimDevice::kInput, true);
Austin Schuh1e69f942020-11-14 15:06:14 -080067 } else {
68 m_analogTrigger = std::make_unique<AnalogTrigger>(m_dutyCycle.get());
69 m_analogTrigger->SetLimitsDutyCycle(0.25, 0.75);
70 m_counter = std::make_unique<Counter>();
71 m_counter->SetUpSource(
72 m_analogTrigger->CreateOutput(AnalogTriggerType::kRisingPulse));
73 m_counter->SetDownSource(
74 m_analogTrigger->CreateOutput(AnalogTriggerType::kFallingPulse));
Brian Silverman8fce7482020-01-05 13:18:21 -080075 }
76
Austin Schuh812d0d12021-11-04 20:16:48 -070077 wpi::SendableRegistry::AddLW(this, "DutyCycle Encoder",
78 m_dutyCycle->GetSourceChannel());
Brian Silverman8fce7482020-01-05 13:18:21 -080079}
80
James Kuszmaulcf324122023-01-14 14:07:17 -080081static bool DoubleEquals(double a, double b) {
82 constexpr double epsilon = 0.00001;
83 return std::abs(a - b) < epsilon;
84}
85
Brian Silverman8fce7482020-01-05 13:18:21 -080086units::turn_t DutyCycleEncoder::Get() const {
Austin Schuh812d0d12021-11-04 20:16:48 -070087 if (m_simPosition) {
88 return units::turn_t{m_simPosition.Get()};
89 }
Brian Silverman8fce7482020-01-05 13:18:21 -080090
91 // As the values are not atomic, keep trying until we get 2 reads of the same
92 // value If we don't within 10 attempts, error
93 for (int i = 0; i < 10; i++) {
Austin Schuh1e69f942020-11-14 15:06:14 -080094 auto counter = m_counter->Get();
Brian Silverman8fce7482020-01-05 13:18:21 -080095 auto pos = m_dutyCycle->GetOutput();
Austin Schuh1e69f942020-11-14 15:06:14 -080096 auto counter2 = m_counter->Get();
Brian Silverman8fce7482020-01-05 13:18:21 -080097 auto pos2 = m_dutyCycle->GetOutput();
James Kuszmaulcf324122023-01-14 14:07:17 -080098 if (counter == counter2 && DoubleEquals(pos, pos2)) {
Austin Schuh75263e32022-02-22 18:05:32 -080099 // map sensor range
James Kuszmaulcf324122023-01-14 14:07:17 -0800100 pos = MapSensorRange(pos);
Brian Silverman8fce7482020-01-05 13:18:21 -0800101 units::turn_t turns{counter + pos - m_positionOffset};
102 m_lastPosition = turns;
103 return turns;
104 }
105 }
106
Austin Schuh812d0d12021-11-04 20:16:48 -0700107 FRC_ReportError(
James Kuszmaulcf324122023-01-14 14:07:17 -0800108 warn::Warning,
Brian Silverman8fce7482020-01-05 13:18:21 -0800109 "Failed to read DutyCycle Encoder. Potential Speed Overrun. Returning "
110 "last value");
111 return m_lastPosition;
112}
113
James Kuszmaulcf324122023-01-14 14:07:17 -0800114double DutyCycleEncoder::MapSensorRange(double pos) const {
115 if (pos < m_sensorMin) {
116 pos = m_sensorMin;
117 }
118 if (pos > m_sensorMax) {
119 pos = m_sensorMax;
120 }
121 pos = (pos - m_sensorMin) / (m_sensorMax - m_sensorMin);
122 return pos;
123}
124
125double DutyCycleEncoder::GetAbsolutePosition() const {
126 if (m_simAbsolutePosition) {
127 return m_simAbsolutePosition.Get();
128 }
129
130 return MapSensorRange(m_dutyCycle->GetOutput());
131}
132
133double DutyCycleEncoder::GetPositionOffset() const {
134 return m_positionOffset;
135}
136
137void DutyCycleEncoder::SetPositionOffset(double offset) {
138 m_positionOffset = std::clamp(offset, 0.0, 1.0);
139}
140
Austin Schuh75263e32022-02-22 18:05:32 -0800141void DutyCycleEncoder::SetDutyCycleRange(double min, double max) {
142 m_sensorMin = std::clamp(min, 0.0, 1.0);
143 m_sensorMax = std::clamp(max, 0.0, 1.0);
144}
145
Brian Silverman8fce7482020-01-05 13:18:21 -0800146void DutyCycleEncoder::SetDistancePerRotation(double distancePerRotation) {
147 m_distancePerRotation = distancePerRotation;
Austin Schuh1e69f942020-11-14 15:06:14 -0800148 m_simDistancePerRotation.Set(distancePerRotation);
Brian Silverman8fce7482020-01-05 13:18:21 -0800149}
150
151double DutyCycleEncoder::GetDistancePerRotation() const {
152 return m_distancePerRotation;
153}
154
155double DutyCycleEncoder::GetDistance() const {
Austin Schuh812d0d12021-11-04 20:16:48 -0700156 return Get().value() * GetDistancePerRotation();
Brian Silverman8fce7482020-01-05 13:18:21 -0800157}
158
159int DutyCycleEncoder::GetFrequency() const {
160 return m_dutyCycle->GetFrequency();
161}
162
163void DutyCycleEncoder::Reset() {
Austin Schuh812d0d12021-11-04 20:16:48 -0700164 if (m_counter) {
165 m_counter->Reset();
166 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800167 m_positionOffset = m_dutyCycle->GetOutput();
168}
169
170bool DutyCycleEncoder::IsConnected() const {
Austin Schuh812d0d12021-11-04 20:16:48 -0700171 if (m_simIsConnected) {
172 return m_simIsConnected.Get();
173 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800174 return GetFrequency() > m_frequencyThreshold;
175}
176
177void DutyCycleEncoder::SetConnectedFrequencyThreshold(int frequency) {
178 if (frequency < 0) {
179 frequency = 0;
180 }
181 m_frequencyThreshold = frequency;
182}
183
Austin Schuh1e69f942020-11-14 15:06:14 -0800184int DutyCycleEncoder::GetFPGAIndex() const {
185 return m_dutyCycle->GetFPGAIndex();
186}
187
188int DutyCycleEncoder::GetSourceChannel() const {
189 return m_dutyCycle->GetSourceChannel();
190}
191
Austin Schuh812d0d12021-11-04 20:16:48 -0700192void DutyCycleEncoder::InitSendable(wpi::SendableBuilder& builder) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800193 builder.SetSmartDashboardType("AbsoluteEncoder");
Austin Schuh1e69f942020-11-14 15:06:14 -0800194 builder.AddDoubleProperty(
195 "Distance", [this] { return this->GetDistance(); }, nullptr);
196 builder.AddDoubleProperty(
197 "Distance Per Rotation",
198 [this] { return this->GetDistancePerRotation(); }, nullptr);
199 builder.AddDoubleProperty(
200 "Is Connected", [this] { return this->IsConnected(); }, nullptr);
Brian Silverman8fce7482020-01-05 13:18:21 -0800201}