blob: ea054ce7902749cbcdd78af3313496bb5c4f1f67 [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()},
James Kuszmaul397f6fe2020-01-04 16:21:52 -080023 m_counter{} {
24 Init();
25}
James Kuszmaul4b81d302019-12-14 20:53:14 -080026
27DutyCycleEncoder::DutyCycleEncoder(DutyCycle& dutyCycle)
28 : m_dutyCycle{&dutyCycle, NullDeleter<DutyCycle>{}},
29 m_analogTrigger{m_dutyCycle.get()},
30 m_counter{} {
31 Init();
32}
33
34DutyCycleEncoder::DutyCycleEncoder(DutyCycle* dutyCycle)
35 : m_dutyCycle{dutyCycle, NullDeleter<DutyCycle>{}},
36 m_analogTrigger{m_dutyCycle.get()},
37 m_counter{} {
38 Init();
39}
40
41DutyCycleEncoder::DutyCycleEncoder(std::shared_ptr<DutyCycle> dutyCycle)
42 : m_dutyCycle{std::move(dutyCycle)},
43 m_analogTrigger{m_dutyCycle.get()},
44 m_counter{} {
45 Init();
46}
47
48DutyCycleEncoder::DutyCycleEncoder(DigitalSource& digitalSource)
49 : m_dutyCycle{std::make_shared<DutyCycle>(digitalSource)},
50 m_analogTrigger{m_dutyCycle.get()},
51 m_counter{} {
52 Init();
53}
54
55DutyCycleEncoder::DutyCycleEncoder(DigitalSource* digitalSource)
56 : m_dutyCycle{std::make_shared<DutyCycle>(digitalSource)},
57 m_analogTrigger{m_dutyCycle.get()},
58 m_counter{} {
59 Init();
60}
61
62DutyCycleEncoder::DutyCycleEncoder(std::shared_ptr<DigitalSource> digitalSource)
63 : m_dutyCycle{std::make_shared<DutyCycle>(digitalSource)},
64 m_analogTrigger{m_dutyCycle.get()},
65 m_counter{} {
66 Init();
67}
68
69void DutyCycleEncoder::Init() {
70 m_simDevice = hal::SimDevice{"DutyCycleEncoder", m_dutyCycle->GetFPGAIndex()};
71
72 if (m_simDevice) {
73 m_simPosition = m_simDevice.CreateDouble("Position", false, 0.0);
74 m_simIsConnected = m_simDevice.CreateBoolean("Connected", false, true);
75 }
76
77 m_analogTrigger.SetLimitsDutyCycle(0.25, 0.75);
78 m_counter.SetUpSource(
79 m_analogTrigger.CreateOutput(AnalogTriggerType::kRisingPulse));
80 m_counter.SetDownSource(
81 m_analogTrigger.CreateOutput(AnalogTriggerType::kFallingPulse));
82
83 SendableRegistry::GetInstance().AddLW(this, "DutyCycle Encoder",
84 m_dutyCycle->GetSourceChannel());
85}
86
87units::turn_t DutyCycleEncoder::Get() const {
88 if (m_simPosition) return units::turn_t{m_simPosition.Get()};
89
90 // As the values are not atomic, keep trying until we get 2 reads of the same
91 // value If we don't within 10 attempts, error
92 for (int i = 0; i < 10; i++) {
93 auto counter = m_counter.Get();
94 auto pos = m_dutyCycle->GetOutput();
95 auto counter2 = m_counter.Get();
96 auto pos2 = m_dutyCycle->GetOutput();
97 if (counter == counter2 && pos == pos2) {
98 units::turn_t turns{counter + pos - m_positionOffset};
99 m_lastPosition = turns;
100 return turns;
101 }
102 }
103
104 frc::DriverStation::GetInstance().ReportWarning(
105 "Failed to read DutyCycle Encoder. Potential Speed Overrun. Returning "
106 "last value");
107 return m_lastPosition;
108}
109
110void DutyCycleEncoder::SetDistancePerRotation(double distancePerRotation) {
111 m_distancePerRotation = distancePerRotation;
112}
113
114double DutyCycleEncoder::GetDistancePerRotation() const {
115 return m_distancePerRotation;
116}
117
118double DutyCycleEncoder::GetDistance() const {
119 return Get().to<double>() * GetDistancePerRotation();
120}
121
122int DutyCycleEncoder::GetFrequency() const {
123 return m_dutyCycle->GetFrequency();
124}
125
126void DutyCycleEncoder::Reset() {
127 m_counter.Reset();
128 m_positionOffset = m_dutyCycle->GetOutput();
129}
130
131bool DutyCycleEncoder::IsConnected() const {
132 if (m_simIsConnected) return m_simIsConnected.Get();
133 return GetFrequency() > m_frequencyThreshold;
134}
135
136void DutyCycleEncoder::SetConnectedFrequencyThreshold(int frequency) {
137 if (frequency < 0) {
138 frequency = 0;
139 }
140 m_frequencyThreshold = frequency;
141}
142
143void DutyCycleEncoder::InitSendable(SendableBuilder& builder) {
144 builder.SetSmartDashboardType("AbsoluteEncoder");
145 builder.AddDoubleProperty("Distance", [this] { return this->GetDistance(); },
146 nullptr);
147 builder.AddDoubleProperty("Distance Per Rotation",
148 [this] { return this->GetDistancePerRotation(); },
149 nullptr);
150 builder.AddDoubleProperty("Is Connected",
151 [this] { return this->IsConnected(); }, nullptr);
152}