blob: ee4f5cc52de510bf4acfe79e9950db39972115d3 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2008-2018 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/Ultrasonic.h"
9
10#include <hal/HAL.h>
11
12#include "frc/Counter.h"
13#include "frc/DigitalInput.h"
14#include "frc/DigitalOutput.h"
15#include "frc/Timer.h"
16#include "frc/Utility.h"
17#include "frc/WPIErrors.h"
18#include "frc/smartdashboard/SendableBuilder.h"
19
20using namespace frc;
21
22// Automatic round robin mode
23std::atomic<bool> Ultrasonic::m_automaticEnabled{false};
24
25std::vector<Ultrasonic*> Ultrasonic::m_sensors;
26std::thread Ultrasonic::m_thread;
27
28Ultrasonic::Ultrasonic(int pingChannel, int echoChannel, DistanceUnit units)
29 : m_pingChannel(std::make_shared<DigitalOutput>(pingChannel)),
30 m_echoChannel(std::make_shared<DigitalInput>(echoChannel)),
31 m_counter(m_echoChannel) {
32 m_units = units;
33 Initialize();
34 AddChild(m_pingChannel);
35 AddChild(m_echoChannel);
36}
37
38Ultrasonic::Ultrasonic(DigitalOutput* pingChannel, DigitalInput* echoChannel,
39 DistanceUnit units)
40 : m_pingChannel(pingChannel, NullDeleter<DigitalOutput>()),
41 m_echoChannel(echoChannel, NullDeleter<DigitalInput>()),
42 m_counter(m_echoChannel) {
43 if (pingChannel == nullptr || echoChannel == nullptr) {
44 wpi_setWPIError(NullParameter);
45 m_units = units;
46 return;
47 }
48 m_units = units;
49 Initialize();
50}
51
52Ultrasonic::Ultrasonic(DigitalOutput& pingChannel, DigitalInput& echoChannel,
53 DistanceUnit units)
54 : m_pingChannel(&pingChannel, NullDeleter<DigitalOutput>()),
55 m_echoChannel(&echoChannel, NullDeleter<DigitalInput>()),
56 m_counter(m_echoChannel) {
57 m_units = units;
58 Initialize();
59}
60
61Ultrasonic::Ultrasonic(std::shared_ptr<DigitalOutput> pingChannel,
62 std::shared_ptr<DigitalInput> echoChannel,
63 DistanceUnit units)
64 : m_pingChannel(pingChannel),
65 m_echoChannel(echoChannel),
66 m_counter(m_echoChannel) {
67 m_units = units;
68 Initialize();
69}
70
71Ultrasonic::~Ultrasonic() {
72 // Delete the instance of the ultrasonic sensor by freeing the allocated
73 // digital channels. If the system was in automatic mode (round robin), then
74 // it is stopped, then started again after this sensor is removed (provided
75 // this wasn't the last sensor).
76
77 bool wasAutomaticMode = m_automaticEnabled;
78 SetAutomaticMode(false);
79
80 // No synchronization needed because the background task is stopped.
81 m_sensors.erase(std::remove(m_sensors.begin(), m_sensors.end(), this),
82 m_sensors.end());
83
84 if (!m_sensors.empty() && wasAutomaticMode) {
85 SetAutomaticMode(true);
86 }
87}
88
89void Ultrasonic::Ping() {
90 wpi_assert(!m_automaticEnabled);
91
92 // Reset the counter to zero (invalid data now)
93 m_counter.Reset();
94
95 // Do the ping to start getting a single range
96 m_pingChannel->Pulse(kPingTime);
97}
98
99bool Ultrasonic::IsRangeValid() const { return m_counter.Get() > 1; }
100
101void Ultrasonic::SetAutomaticMode(bool enabling) {
102 if (enabling == m_automaticEnabled) return; // ignore the case of no change
103
104 m_automaticEnabled = enabling;
105
106 if (enabling) {
107 /* Clear all the counters so no data is valid. No synchronization is needed
108 * because the background task is stopped.
109 */
110 for (auto& sensor : m_sensors) {
111 sensor->m_counter.Reset();
112 }
113
114 m_thread = std::thread(&Ultrasonic::UltrasonicChecker);
115
116 // TODO: Currently, lvuser does not have permissions to set task priorities.
117 // Until that is the case, uncommenting this will break user code that calls
118 // Ultrasonic::SetAutomicMode().
119 // m_task.SetPriority(kPriority);
120 } else {
121 // Wait for background task to stop running
122 if (m_thread.joinable()) {
123 m_thread.join();
124 }
125
126 // Clear all the counters (data now invalid) since automatic mode is
127 // disabled. No synchronization is needed because the background task is
128 // stopped.
129 for (auto& sensor : m_sensors) {
130 sensor->m_counter.Reset();
131 }
132 }
133}
134
135double Ultrasonic::GetRangeInches() const {
136 if (IsRangeValid())
137 return m_counter.GetPeriod() * kSpeedOfSoundInchesPerSec / 2.0;
138 else
139 return 0;
140}
141
142double Ultrasonic::GetRangeMM() const { return GetRangeInches() * 25.4; }
143
144bool Ultrasonic::IsEnabled() const { return m_enabled; }
145
146void Ultrasonic::SetEnabled(bool enable) { m_enabled = enable; }
147
148void Ultrasonic::SetDistanceUnits(DistanceUnit units) { m_units = units; }
149
150Ultrasonic::DistanceUnit Ultrasonic::GetDistanceUnits() const {
151 return m_units;
152}
153
154double Ultrasonic::PIDGet() {
155 switch (m_units) {
156 case Ultrasonic::kInches:
157 return GetRangeInches();
158 case Ultrasonic::kMilliMeters:
159 return GetRangeMM();
160 default:
161 return 0.0;
162 }
163}
164
165void Ultrasonic::SetPIDSourceType(PIDSourceType pidSource) {
166 if (wpi_assert(pidSource == PIDSourceType::kDisplacement)) {
167 m_pidSource = pidSource;
168 }
169}
170
171void Ultrasonic::InitSendable(SendableBuilder& builder) {
172 builder.SetSmartDashboardType("Ultrasonic");
173 builder.AddDoubleProperty("Value", [=]() { return GetRangeInches(); },
174 nullptr);
175}
176
177void Ultrasonic::Initialize() {
178 bool originalMode = m_automaticEnabled;
179 SetAutomaticMode(false); // Kill task when adding a new sensor
180 // Link this instance on the list
181 m_sensors.emplace_back(this);
182
183 m_counter.SetMaxPeriod(1.0);
184 m_counter.SetSemiPeriodMode(true);
185 m_counter.Reset();
186 m_enabled = true; // Make it available for round robin scheduling
187 SetAutomaticMode(originalMode);
188
189 static int instances = 0;
190 instances++;
191 HAL_Report(HALUsageReporting::kResourceType_Ultrasonic, instances);
192 SetName("Ultrasonic", m_echoChannel->GetChannel());
193}
194
195void Ultrasonic::UltrasonicChecker() {
196 while (m_automaticEnabled) {
197 for (auto& sensor : m_sensors) {
198 if (!m_automaticEnabled) break;
199
200 if (sensor->IsEnabled()) {
201 sensor->m_pingChannel->Pulse(kPingTime); // do the ping
202 }
203
204 Wait(0.1); // wait for ping to return
205 }
206 }
207}