blob: 1239eb2e4ee928fb77d0f44105a90c19724cc47b [file] [log] [blame]
Austin Schuh59d93f42022-07-18 16:52:32 -07001#include "absl/strings/str_cat.h"
2#include "absl/strings/str_split.h"
3#include "aos/init.h"
4#include "aos/util/file.h"
5#include "frc971/analysis/in_process_plotter.h"
6
7using frc971::analysis::Plotter;
8
Austin Schuh69d0b732022-07-20 21:19:32 -07009DEFINE_bool(all, false, "If true, plot *all* the nodes at once");
10DEFINE_bool(bounds, false, "If true, plot the noncausal bounds too.");
11DEFINE_bool(samples, true, "If true, plot the samples too.");
12
13DEFINE_string(offsets, "",
14 "Offsets to add to the monotonic clock for each node. Use the "
15 "format of node=offset,node=offest");
16
Austin Schuh59d93f42022-07-18 16:52:32 -070017// Simple C++ application to read the CSV files and use the in process plotter
18// to plot them. This smokes the pants off gnuplot in terms of interactivity.
19
20namespace aos {
21
Austin Schuh69d0b732022-07-20 21:19:32 -070022// Returns all the nodes.
23std::vector<std::string> Nodes() {
24 const std::string start_time_file = aos::util::ReadFileToStringOrDie(
25 "/tmp/timestamp_noncausal_starttime.csv");
26 std::vector<std::string_view> nodes = absl::StrSplit(start_time_file, '\n');
27
28 std::vector<std::string> formatted_nodes;
29 for (const std::string_view n : nodes) {
30 if (n == "") {
31 continue;
32 }
33
34 std::vector<std::string_view> l = absl::StrSplit(n, ", ");
35 CHECK_EQ(l.size(), 2u) << "'" << n << "'";
36 formatted_nodes.emplace_back(l[0]);
37 }
38
39 return formatted_nodes;
40}
41
42std::string SampleFile(std::string_view node1, std::string_view node2) {
43 return absl::StrCat("/tmp/timestamp_noncausal_", node1, "_", node2,
44 "_samples.csv");
45}
46
Austin Schuh59d93f42022-07-18 16:52:32 -070047std::pair<std::vector<double>, std::vector<double>> ReadSamples(
48 std::string_view node1, std::string_view node2, bool flip) {
49 std::vector<double> samplefile12_t;
50 std::vector<double> samplefile12_o;
51
Austin Schuh69d0b732022-07-20 21:19:32 -070052 const std::string file =
53 aos::util::ReadFileToStringOrDie(SampleFile(node1, node2));
Austin Schuh59d93f42022-07-18 16:52:32 -070054 bool first = true;
55 std::vector<std::string_view> lines = absl::StrSplit(file, '\n');
56 samplefile12_t.reserve(lines.size());
57 for (const std::string_view n : lines) {
58 if (first) {
59 first = false;
60 continue;
61 }
62 if (n == "") {
63 continue;
64 }
65
66 std::vector<std::string_view> l = absl::StrSplit(n, ", ");
Austin Schuh8c2e8c72022-08-06 18:31:19 -070067 if (l.size() != 4u) {
68 continue;
69 }
Austin Schuh59d93f42022-07-18 16:52:32 -070070 double t;
71 double o;
72 CHECK(absl::SimpleAtod(l[0], &t));
73 CHECK(absl::SimpleAtod(l[1], &o));
74 samplefile12_t.emplace_back(t);
75 samplefile12_o.emplace_back(flip ? -o : o);
76 }
77 return std::make_pair(samplefile12_t, samplefile12_o);
78}
79
Austin Schuh69d0b732022-07-20 21:19:32 -070080void Offset(std::vector<double> *v, double offset) {
81 for (double &x : *v) {
82 x += offset;
83 }
84}
85
86// Returns all the nodes which talk to each other.
87std::vector<std::pair<std::string, std::string>> NodeConnections() {
88 const std::vector<std::string> nodes = Nodes();
89 std::vector<std::pair<std::string, std::string>> result;
90 for (size_t i = 1; i < nodes.size(); ++i) {
91 for (size_t j = 0; j < i; ++j) {
92 const std::string_view node1 = nodes[j];
93 const std::string_view node2 = nodes[i];
94 if (aos::util::PathExists(SampleFile(node1, node2))) {
95 result.emplace_back(node1, node2);
96 LOG(INFO) << "Found pairing " << node1 << ", " << node2;
97 }
98 }
99 }
100 return result;
101}
102
103// Class to encapsulate the plotter state to make it easy to plot multiple
104// connections.
105class NodePlotter {
106 public:
107 NodePlotter() : nodes_(Nodes()) {
108 plotter_.AddFigure("Time");
109 if (!FLAGS_offsets.empty()) {
110 for (std::string_view nodeoffset : absl::StrSplit(FLAGS_offsets, ',')) {
111 std::vector<std::string_view> node_offset =
112 absl::StrSplit(nodeoffset, '=');
113 CHECK_EQ(node_offset.size(), 2u);
114 double o;
115 CHECK(absl::SimpleAtod(node_offset[1], &o));
116 offset_.emplace(std::string(node_offset[0]), o);
117 }
118 }
119 }
120
121 void AddNodes(std::string_view node1, std::string_view node2);
122
123 void Serve() {
124 plotter_.Publish();
125 plotter_.Spin();
126 }
127
128 private:
129 std::pair<std::vector<double>, std::vector<double>> ReadLines(
130 std::string_view node1, std::string_view node2, bool flip);
131
132 std::pair<std::vector<double>, std::vector<double>> ReadOffset(
133 std::string_view node1, std::string_view node2);
134
135 double TimeOffset(std::string_view node) {
136 auto it = offset_.find(std::string(node));
137 if (it == offset_.end()) {
138 return 0.0;
139 } else {
140 return it->second;
141 }
142 }
143
144 std::map<std::string, double> offset_;
145
146 Plotter plotter_;
147
148 std::vector<std::string> nodes_;
149};
150
151std::pair<std::vector<double>, std::vector<double>> NodePlotter::ReadLines(
Austin Schuh59d93f42022-07-18 16:52:32 -0700152 std::string_view node1, std::string_view node2, bool flip) {
153 std::vector<double> samplefile12_t;
154 std::vector<double> samplefile12_o;
155
156 const std::string file = aos::util::ReadFileToStringOrDie(
157 absl::StrCat("/tmp/timestamp_noncausal_", node1, "_", node2, ".csv"));
158 bool first = true;
159 std::vector<std::string_view> lines = absl::StrSplit(file, '\n');
160 samplefile12_t.reserve(lines.size());
161 for (const std::string_view n : lines) {
162 if (first) {
163 first = false;
164 continue;
165 }
166 if (n == "") {
167 continue;
168 }
169
170 std::vector<std::string_view> l = absl::StrSplit(n, ", ");
Austin Schuh8c2e8c72022-08-06 18:31:19 -0700171 if (l.size() != 3u) {
172 continue;
173 }
Austin Schuh59d93f42022-07-18 16:52:32 -0700174 double t;
175 double o;
176 CHECK(absl::SimpleAtod(l[0], &t));
177 CHECK(absl::SimpleAtod(l[2], &o));
178 samplefile12_t.emplace_back(t);
179 samplefile12_o.emplace_back(flip ? -o : o);
180 }
181 return std::make_pair(samplefile12_t, samplefile12_o);
182}
183
Austin Schuh69d0b732022-07-20 21:19:32 -0700184std::pair<std::vector<double>, std::vector<double>> NodePlotter::ReadOffset(
Austin Schuh59d93f42022-07-18 16:52:32 -0700185 std::string_view node1, std::string_view node2) {
186 int node1_index = -1;
187 int node2_index = -1;
188
189 {
Austin Schuh59d93f42022-07-18 16:52:32 -0700190 int index = 0;
Austin Schuh69d0b732022-07-20 21:19:32 -0700191 for (const std::string &n : nodes_) {
192 if (n == node1) {
Austin Schuh59d93f42022-07-18 16:52:32 -0700193 node1_index = index;
194 }
Austin Schuh69d0b732022-07-20 21:19:32 -0700195 if (n == node2) {
Austin Schuh59d93f42022-07-18 16:52:32 -0700196 node2_index = index;
197 }
198 ++index;
199 }
200 }
201 CHECK_NE(node1_index, -1) << ": Unknown node " << node1;
202 CHECK_NE(node2_index, -1) << ": Unknown node " << node2;
203 std::vector<double> offsetfile_t;
204 std::vector<double> offsetfile_o;
205
206 const std::string file =
207 aos::util::ReadFileToStringOrDie("/tmp/timestamp_noncausal_offsets.csv");
208 bool first = true;
209 std::vector<std::string_view> lines = absl::StrSplit(file, '\n');
210 offsetfile_t.reserve(lines.size());
211 for (const std::string_view n : lines) {
212 if (first) {
213 first = false;
214 continue;
215 }
216 if (n == "") {
217 continue;
218 }
219
220 std::vector<std::string_view> l = absl::StrSplit(n, ", ");
221 CHECK_LT(static_cast<size_t>(node1_index + 1), l.size());
222 CHECK_LT(static_cast<size_t>(node2_index + 1), l.size());
223 double t;
224 double o1;
225 double o2;
226 CHECK(absl::SimpleAtod(l[0], &t));
227 CHECK(absl::SimpleAtod(l[1 + node1_index], &o1));
228 CHECK(absl::SimpleAtod(l[1 + node2_index], &o2));
229 offsetfile_t.emplace_back(t);
230 offsetfile_o.emplace_back(o2 - o1);
231 }
232 return std::make_pair(offsetfile_t, offsetfile_o);
233}
234
Austin Schuh69d0b732022-07-20 21:19:32 -0700235void NodePlotter::AddNodes(std::string_view node1, std::string_view node2) {
236 const double offset1 = TimeOffset(node1);
237 const double offset2 = TimeOffset(node2);
238
239 std::pair<std::vector<double>, std::vector<double>> samplefile12 =
Austin Schuh59d93f42022-07-18 16:52:32 -0700240 ReadSamples(node1, node2, false);
Austin Schuh69d0b732022-07-20 21:19:32 -0700241 std::pair<std::vector<double>, std::vector<double>> samplefile21 =
Austin Schuh59d93f42022-07-18 16:52:32 -0700242 ReadSamples(node2, node1, true);
243
Austin Schuh69d0b732022-07-20 21:19:32 -0700244 std::pair<std::vector<double>, std::vector<double>> noncausalfile12 =
Austin Schuh59d93f42022-07-18 16:52:32 -0700245 ReadLines(node1, node2, false);
Austin Schuh69d0b732022-07-20 21:19:32 -0700246 std::pair<std::vector<double>, std::vector<double>> noncausalfile21 =
Austin Schuh59d93f42022-07-18 16:52:32 -0700247 ReadLines(node2, node1, true);
248
Austin Schuh69d0b732022-07-20 21:19:32 -0700249 std::pair<std::vector<double>, std::vector<double>> offsetfile =
Austin Schuh59d93f42022-07-18 16:52:32 -0700250 ReadOffset(node1, node2);
251
Austin Schuh69d0b732022-07-20 21:19:32 -0700252 Offset(&samplefile12.second, offset2 - offset1);
253 Offset(&samplefile21.second, offset2 - offset1);
254 Offset(&noncausalfile12.second, offset2 - offset1);
255 Offset(&noncausalfile21.second, offset2 - offset1);
256 Offset(&offsetfile.second, offset2 - offset1);
257
Austin Schuh59d93f42022-07-18 16:52:32 -0700258 CHECK_EQ(samplefile12.first.size(), samplefile12.second.size());
259 CHECK_EQ(samplefile21.first.size(), samplefile21.second.size());
260 CHECK_EQ(noncausalfile12.first.size(), noncausalfile12.second.size());
261 CHECK_EQ(noncausalfile21.first.size(), noncausalfile21.second.size());
262
263 LOG(INFO) << samplefile12.first.size() + samplefile21.first.size() +
264 noncausalfile12.first.size() + noncausalfile21.first.size()
265 << " points";
Austin Schuh59d93f42022-07-18 16:52:32 -0700266
Austin Schuh69d0b732022-07-20 21:19:32 -0700267 plotter_.AddLine(offsetfile.first, offsetfile.second,
Austin Schuh59d93f42022-07-18 16:52:32 -0700268 Plotter::LineOptions{
269 .label = absl::StrCat("filter ", node2, " ", node1),
270 // TODO(austin): roboRIO compiler wants all the fields
271 // filled out, but other compilers don't... Sigh.
272 .line_style = "*-",
Austin Schuh69d0b732022-07-20 21:19:32 -0700273 .color = "yellow",
274 .point_size = 2.0});
275
276 if (FLAGS_samples) {
277 plotter_.AddLine(samplefile12.first, samplefile12.second,
278 Plotter::LineOptions{
279 .label = absl::StrCat("sample ", node1, " ", node2),
280 .line_style = "*",
281 .color = "purple",
282 });
283 plotter_.AddLine(samplefile21.first, samplefile21.second,
284 Plotter::LineOptions{
285 .label = absl::StrCat("sample ", node2, " ", node1),
286 .line_style = "*",
287 .color = "green",
288 });
289 }
290
291 if (FLAGS_bounds) {
292 plotter_.AddLine(
293 noncausalfile12.first, noncausalfile12.second,
294 Plotter::LineOptions{.label = absl::StrCat("nc ", node1, " ", node2),
295 .line_style = "-",
296 .color = "blue"});
297 plotter_.AddLine(
298 noncausalfile21.first, noncausalfile21.second,
299 Plotter::LineOptions{.label = absl::StrCat("nc ", node2, " ", node1),
300 .line_style = "-",
301 .color = "orange"});
302 }
Austin Schuh59d93f42022-07-18 16:52:32 -0700303}
304
305int Main(int argc, const char *const *argv) {
Austin Schuh69d0b732022-07-20 21:19:32 -0700306 NodePlotter plotter;
Austin Schuh59d93f42022-07-18 16:52:32 -0700307
Austin Schuh69d0b732022-07-20 21:19:32 -0700308 if (FLAGS_all) {
309 for (std::pair<std::string, std::string> ab : NodeConnections()) {
310 plotter.AddNodes(ab.first, ab.second);
311 }
312 } else {
313 CHECK_EQ(argc, 3);
Austin Schuh59d93f42022-07-18 16:52:32 -0700314
Austin Schuh69d0b732022-07-20 21:19:32 -0700315 LOG(INFO) << argv[1];
316 LOG(INFO) << argv[2];
Austin Schuh59d93f42022-07-18 16:52:32 -0700317
Austin Schuh69d0b732022-07-20 21:19:32 -0700318 const std::string_view node1 = argv[1];
319 const std::string_view node2 = argv[2];
Austin Schuh59d93f42022-07-18 16:52:32 -0700320
Austin Schuh69d0b732022-07-20 21:19:32 -0700321 plotter.AddNodes(node1, node2);
322 }
Austin Schuh59d93f42022-07-18 16:52:32 -0700323
Austin Schuh69d0b732022-07-20 21:19:32 -0700324 plotter.Serve();
Austin Schuh59d93f42022-07-18 16:52:32 -0700325
326 return 0;
327}
328
329} // namespace aos
330
331int main(int argc, char **argv) {
332 aos::InitGoogle(&argc, &argv);
333
334 aos::Main(argc, argv);
335}