blob: 728b1def7dda5c5eb3ecb89b6a28520db1f86e03 [file] [log] [blame]
James Kuszmaul4b81d302019-12-14 20:53:14 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2019 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "frc/DutyCycleEncoder.h"
9
10#include "frc/Counter.h"
11#include "frc/DigitalInput.h"
12#include "frc/DigitalSource.h"
13#include "frc/DriverStation.h"
14#include "frc/DutyCycle.h"
15#include "frc/smartdashboard/SendableBuilder.h"
16
17using namespace frc;
18
19DutyCycleEncoder::DutyCycleEncoder(int channel)
20 : m_dutyCycle{std::make_shared<DutyCycle>(
21 std::make_shared<DigitalInput>(channel))},
22 m_analogTrigger{m_dutyCycle.get()},
23 m_counter{} {}
24
25DutyCycleEncoder::DutyCycleEncoder(DutyCycle& dutyCycle)
26 : m_dutyCycle{&dutyCycle, NullDeleter<DutyCycle>{}},
27 m_analogTrigger{m_dutyCycle.get()},
28 m_counter{} {
29 Init();
30}
31
32DutyCycleEncoder::DutyCycleEncoder(DutyCycle* dutyCycle)
33 : m_dutyCycle{dutyCycle, NullDeleter<DutyCycle>{}},
34 m_analogTrigger{m_dutyCycle.get()},
35 m_counter{} {
36 Init();
37}
38
39DutyCycleEncoder::DutyCycleEncoder(std::shared_ptr<DutyCycle> dutyCycle)
40 : m_dutyCycle{std::move(dutyCycle)},
41 m_analogTrigger{m_dutyCycle.get()},
42 m_counter{} {
43 Init();
44}
45
46DutyCycleEncoder::DutyCycleEncoder(DigitalSource& digitalSource)
47 : m_dutyCycle{std::make_shared<DutyCycle>(digitalSource)},
48 m_analogTrigger{m_dutyCycle.get()},
49 m_counter{} {
50 Init();
51}
52
53DutyCycleEncoder::DutyCycleEncoder(DigitalSource* digitalSource)
54 : m_dutyCycle{std::make_shared<DutyCycle>(digitalSource)},
55 m_analogTrigger{m_dutyCycle.get()},
56 m_counter{} {
57 Init();
58}
59
60DutyCycleEncoder::DutyCycleEncoder(std::shared_ptr<DigitalSource> digitalSource)
61 : m_dutyCycle{std::make_shared<DutyCycle>(digitalSource)},
62 m_analogTrigger{m_dutyCycle.get()},
63 m_counter{} {
64 Init();
65}
66
67void DutyCycleEncoder::Init() {
68 m_simDevice = hal::SimDevice{"DutyCycleEncoder", m_dutyCycle->GetFPGAIndex()};
69
70 if (m_simDevice) {
71 m_simPosition = m_simDevice.CreateDouble("Position", false, 0.0);
72 m_simIsConnected = m_simDevice.CreateBoolean("Connected", false, true);
73 }
74
75 m_analogTrigger.SetLimitsDutyCycle(0.25, 0.75);
76 m_counter.SetUpSource(
77 m_analogTrigger.CreateOutput(AnalogTriggerType::kRisingPulse));
78 m_counter.SetDownSource(
79 m_analogTrigger.CreateOutput(AnalogTriggerType::kFallingPulse));
80
81 SendableRegistry::GetInstance().AddLW(this, "DutyCycle Encoder",
82 m_dutyCycle->GetSourceChannel());
83}
84
85units::turn_t DutyCycleEncoder::Get() const {
86 if (m_simPosition) return units::turn_t{m_simPosition.Get()};
87
88 // As the values are not atomic, keep trying until we get 2 reads of the same
89 // value If we don't within 10 attempts, error
90 for (int i = 0; i < 10; i++) {
91 auto counter = m_counter.Get();
92 auto pos = m_dutyCycle->GetOutput();
93 auto counter2 = m_counter.Get();
94 auto pos2 = m_dutyCycle->GetOutput();
95 if (counter == counter2 && pos == pos2) {
96 units::turn_t turns{counter + pos - m_positionOffset};
97 m_lastPosition = turns;
98 return turns;
99 }
100 }
101
102 frc::DriverStation::GetInstance().ReportWarning(
103 "Failed to read DutyCycle Encoder. Potential Speed Overrun. Returning "
104 "last value");
105 return m_lastPosition;
106}
107
108void DutyCycleEncoder::SetDistancePerRotation(double distancePerRotation) {
109 m_distancePerRotation = distancePerRotation;
110}
111
112double DutyCycleEncoder::GetDistancePerRotation() const {
113 return m_distancePerRotation;
114}
115
116double DutyCycleEncoder::GetDistance() const {
117 return Get().to<double>() * GetDistancePerRotation();
118}
119
120int DutyCycleEncoder::GetFrequency() const {
121 return m_dutyCycle->GetFrequency();
122}
123
124void DutyCycleEncoder::Reset() {
125 m_counter.Reset();
126 m_positionOffset = m_dutyCycle->GetOutput();
127}
128
129bool DutyCycleEncoder::IsConnected() const {
130 if (m_simIsConnected) return m_simIsConnected.Get();
131 return GetFrequency() > m_frequencyThreshold;
132}
133
134void DutyCycleEncoder::SetConnectedFrequencyThreshold(int frequency) {
135 if (frequency < 0) {
136 frequency = 0;
137 }
138 m_frequencyThreshold = frequency;
139}
140
141void DutyCycleEncoder::InitSendable(SendableBuilder& builder) {
142 builder.SetSmartDashboardType("AbsoluteEncoder");
143 builder.AddDoubleProperty("Distance", [this] { return this->GetDistance(); },
144 nullptr);
145 builder.AddDoubleProperty("Distance Per Rotation",
146 [this] { return this->GetDistancePerRotation(); },
147 nullptr);
148 builder.AddDoubleProperty("Is Connected",
149 [this] { return this->IsConnected(); }, nullptr);
150}