blob: 452f5cd729234bbfb4fe2eb4de6ff1f15ef00214 [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);
63 m_simIsConnected =
64 m_simDevice.CreateBoolean("connected", hal::SimDevice::kInput, true);
Austin Schuh1e69f942020-11-14 15:06:14 -080065 } else {
66 m_analogTrigger = std::make_unique<AnalogTrigger>(m_dutyCycle.get());
67 m_analogTrigger->SetLimitsDutyCycle(0.25, 0.75);
68 m_counter = std::make_unique<Counter>();
69 m_counter->SetUpSource(
70 m_analogTrigger->CreateOutput(AnalogTriggerType::kRisingPulse));
71 m_counter->SetDownSource(
72 m_analogTrigger->CreateOutput(AnalogTriggerType::kFallingPulse));
Brian Silverman8fce7482020-01-05 13:18:21 -080073 }
74
Austin Schuh812d0d12021-11-04 20:16:48 -070075 wpi::SendableRegistry::AddLW(this, "DutyCycle Encoder",
76 m_dutyCycle->GetSourceChannel());
Brian Silverman8fce7482020-01-05 13:18:21 -080077}
78
79units::turn_t DutyCycleEncoder::Get() const {
Austin Schuh812d0d12021-11-04 20:16:48 -070080 if (m_simPosition) {
81 return units::turn_t{m_simPosition.Get()};
82 }
Brian Silverman8fce7482020-01-05 13:18:21 -080083
84 // As the values are not atomic, keep trying until we get 2 reads of the same
85 // value If we don't within 10 attempts, error
86 for (int i = 0; i < 10; i++) {
Austin Schuh1e69f942020-11-14 15:06:14 -080087 auto counter = m_counter->Get();
Brian Silverman8fce7482020-01-05 13:18:21 -080088 auto pos = m_dutyCycle->GetOutput();
Austin Schuh1e69f942020-11-14 15:06:14 -080089 auto counter2 = m_counter->Get();
Brian Silverman8fce7482020-01-05 13:18:21 -080090 auto pos2 = m_dutyCycle->GetOutput();
91 if (counter == counter2 && pos == pos2) {
Austin Schuh75263e32022-02-22 18:05:32 -080092 // map sensor range
93 if (pos < m_sensorMin) {
94 pos = m_sensorMin;
95 }
96 if (pos > m_sensorMax) {
97 pos = m_sensorMax;
98 }
99 pos = (pos - m_sensorMin) / (m_sensorMax - m_sensorMin);
Brian Silverman8fce7482020-01-05 13:18:21 -0800100 units::turn_t turns{counter + pos - m_positionOffset};
101 m_lastPosition = turns;
102 return turns;
103 }
104 }
105
Austin Schuh812d0d12021-11-04 20:16:48 -0700106 FRC_ReportError(
107 warn::Warning, "{}",
Brian Silverman8fce7482020-01-05 13:18:21 -0800108 "Failed to read DutyCycle Encoder. Potential Speed Overrun. Returning "
109 "last value");
110 return m_lastPosition;
111}
112
Austin Schuh75263e32022-02-22 18:05:32 -0800113void DutyCycleEncoder::SetDutyCycleRange(double min, double max) {
114 m_sensorMin = std::clamp(min, 0.0, 1.0);
115 m_sensorMax = std::clamp(max, 0.0, 1.0);
116}
117
Brian Silverman8fce7482020-01-05 13:18:21 -0800118void DutyCycleEncoder::SetDistancePerRotation(double distancePerRotation) {
119 m_distancePerRotation = distancePerRotation;
Austin Schuh1e69f942020-11-14 15:06:14 -0800120 m_simDistancePerRotation.Set(distancePerRotation);
Brian Silverman8fce7482020-01-05 13:18:21 -0800121}
122
123double DutyCycleEncoder::GetDistancePerRotation() const {
124 return m_distancePerRotation;
125}
126
127double DutyCycleEncoder::GetDistance() const {
Austin Schuh812d0d12021-11-04 20:16:48 -0700128 return Get().value() * GetDistancePerRotation();
Brian Silverman8fce7482020-01-05 13:18:21 -0800129}
130
131int DutyCycleEncoder::GetFrequency() const {
132 return m_dutyCycle->GetFrequency();
133}
134
135void DutyCycleEncoder::Reset() {
Austin Schuh812d0d12021-11-04 20:16:48 -0700136 if (m_counter) {
137 m_counter->Reset();
138 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800139 m_positionOffset = m_dutyCycle->GetOutput();
140}
141
142bool DutyCycleEncoder::IsConnected() const {
Austin Schuh812d0d12021-11-04 20:16:48 -0700143 if (m_simIsConnected) {
144 return m_simIsConnected.Get();
145 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800146 return GetFrequency() > m_frequencyThreshold;
147}
148
149void DutyCycleEncoder::SetConnectedFrequencyThreshold(int frequency) {
150 if (frequency < 0) {
151 frequency = 0;
152 }
153 m_frequencyThreshold = frequency;
154}
155
Austin Schuh1e69f942020-11-14 15:06:14 -0800156int DutyCycleEncoder::GetFPGAIndex() const {
157 return m_dutyCycle->GetFPGAIndex();
158}
159
160int DutyCycleEncoder::GetSourceChannel() const {
161 return m_dutyCycle->GetSourceChannel();
162}
163
Austin Schuh812d0d12021-11-04 20:16:48 -0700164void DutyCycleEncoder::InitSendable(wpi::SendableBuilder& builder) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800165 builder.SetSmartDashboardType("AbsoluteEncoder");
Austin Schuh1e69f942020-11-14 15:06:14 -0800166 builder.AddDoubleProperty(
167 "Distance", [this] { return this->GetDistance(); }, nullptr);
168 builder.AddDoubleProperty(
169 "Distance Per Rotation",
170 [this] { return this->GetDistancePerRotation(); }, nullptr);
171 builder.AddDoubleProperty(
172 "Is Connected", [this] { return this->IsConnected(); }, nullptr);
Brian Silverman8fce7482020-01-05 13:18:21 -0800173}