0){var i=G[n-1],r=p[i.a],s=p[i.b];r.size=t[n-1],s.size=e,F(r.element,r.size,i._b,r.i),F(s.element,s.size,i._c,s.i)}}))},getSizes:R,collapse:function(e){J(p[e])},destroy:function(e,t){G.forEach((function(n){if(!0!==t?n.parent.removeChild(n.gutter):(n.gutter.removeEventListener("mousedown",n._a),n.gutter.removeEventListener("touchstart",n._a)),!0!==e){var i=T(d,n.a.size,n._b);Object.keys(i).forEach((function(e){p[n.a].element.style[e]="",p[n.b].element.style[e]=""}))}}))},parent:z,pairs:G}}}));
24 | //# sourceMappingURL=split.min.js.map
25 |
--------------------------------------------------------------------------------
/ardw-app/static/study.js:
--------------------------------------------------------------------------------
1 | var socket = io();
2 |
3 | var first_with = {
4 | "1A": {
5 | "with": document.getElementById("first-with-1a"),
6 | "without": document.getElementById("first-without-1a"),
7 | },
8 | "1B": {
9 | "with": document.getElementById("first-with-1b"),
10 | "without": document.getElementById("first-without-1b"),
11 | }
12 | }
13 |
14 | for (let task in first_with) {
15 | first_with[task].with.addEventListener("click", () => {
16 | socket.emit("study-event", {"event": "settings", "task": task, "first_with": true});
17 | });
18 | first_with[task].without.addEventListener("click", () => {
19 | socket.emit("study-event", {"event": "settings", "task": task, "first_with": false});
20 | });
21 | }
22 |
23 | var task_buttons = {
24 | "off": document.getElementById("task-off"),
25 | "1A": document.getElementById("task-1a"),
26 | "1B": document.getElementById("task-1b"),
27 | "2": document.getElementById("task-2"),
28 | }
29 |
30 | task_buttons["off"].addEventListener("click", () => {
31 | socket.emit("study-event", {"event": "task", "task": "off"})
32 | })
33 | task_buttons["1A"].addEventListener("click", () => {
34 | socket.emit("study-event", {"event": "task", "task": "1A"})
35 | })
36 | task_buttons["1B"].addEventListener("click", () => {
37 | socket.emit("study-event", {"event": "task", "task": "1B"})
38 | })
39 | task_buttons["2"].addEventListener("click", () => {
40 | socket.emit("study-event", {"event": "task", "task": "2"})
41 | })
42 |
43 | var next_button = document.getElementById("next");
44 | next_button.addEventListener("click", goNext)
45 |
46 | var step_text = document.getElementById("step");
47 | var status_text = document.getElementById("status");
48 | var boardviz_text = document.getElementById("boardviz");
49 | var comp_text = document.getElementById("comp");
50 |
51 | var custom_input = document.getElementById("custom-input");
52 | custom_input.addEventListener("keydown", (evt) => {
53 | if (evt.key === "Enter") {
54 | submitCustom();
55 | }
56 | })
57 | var custom_submit = document.getElementById("custom-btn");
58 | custom_submit.addEventListener("click", submitCustom);
59 |
60 | window.addEventListener("keydown", (evt) => {
61 | if (evt.key == "n" && document.activeElement !== custom_input) {
62 | goNext();
63 | }
64 | })
65 |
66 |
67 | var timer_text = document.getElementById("timer-text");
68 | var timer_btn = document.getElementById("timer-btn");
69 |
70 | const TIMER_HZ = 20;
71 | var timer_on = false;
72 | var timer_task = null;
73 | var timer_val = 0;
74 | timer_btn.addEventListener("click", () => {
75 | socket.emit("study-event", {"event": "timer", "turn_on": !timer_on})
76 | })
77 |
78 | /** each has HTML elements btn, xin, yin and values x, y where
79 | * x and y are in "mm", but technically in optitrack pixels */
80 | var probe_elements = {
81 | "red": {},
82 | "grey": {},
83 | }
84 | /** null, "red", or "grey" */
85 | var probe_listening = null;
86 |
87 | function probe_btn_click(color) {
88 | probe_elements.red.btn.classList.remove("selected");
89 | probe_elements.grey.btn.classList.remove("selected");
90 | probe_elements.red.btn.innerText = "Off";
91 | probe_elements.grey.btn.innerText = "Off";
92 |
93 | if (probe_listening === color) {
94 | probe_listening = null;
95 | } else {
96 | probe_listening = color;
97 | probe_elements[color].btn.classList.add("selected");
98 | probe_elements[color].btn.innerText = "On";
99 | }
100 | }
101 |
102 | function probe_input(evt, color) {
103 | if (evt.key === "Enter") {
104 | var xoff = parseFloat(probe_elements[color].xin.value);
105 | var yoff = parseFloat(probe_elements[color].yin.value);
106 | if (isNaN(xoff)) xoff = 0;
107 | if (isNaN(yoff)) yoff = 0;
108 | socket.emit("probe-adjust", {"probe": color, "x": xoff, "y": yoff});
109 | }
110 | }
111 |
112 | for (let color in probe_elements) {
113 | probe_elements[color]["btn"] = document.getElementById(`${color}-btn`);
114 | probe_elements[color]["xin"] = document.getElementById(`${color}-x`);
115 | probe_elements[color]["yin"] = document.getElementById(`${color}-y`);
116 | probe_elements[color]["x"] = 0;
117 | probe_elements[color]["y"] = 0;
118 |
119 | probe_elements[color].btn.addEventListener("click", () => {
120 | probe_btn_click(color);
121 | });
122 | probe_elements[color].xin.addEventListener("keydown", (evt) => {
123 | probe_input(evt, color);
124 | });
125 | probe_elements[color].yin.addEventListener("keydown", (evt) => {
126 | probe_input(evt, color);
127 | });
128 | }
129 |
130 | window.addEventListener("keydown", (evt) => {
131 | if (document.activeElement !== custom_input && probe_listening !== null) {
132 | var did_something = false;
133 | var x = probe_elements[probe_listening].x;
134 | var y = probe_elements[probe_listening].y;
135 | switch (evt.key) {
136 | case "w":
137 | y -= 0.1;
138 | did_something = true;
139 | break;
140 | case "s":
141 | y += 0.1;
142 | did_something = true;
143 | break;
144 | case "a":
145 | x -= 0.1;
146 | did_something = true;
147 | break;
148 | case "d":
149 | x += 0.1;
150 | did_something = true;
151 | break;
152 | }
153 | if (did_something) {
154 | socket.emit("probe-adjust", {"probe": probe_listening, "x": x, "y": y});
155 | }
156 | }
157 | })
158 |
159 |
160 | function goNext() {
161 | socket.emit("study-event", {"event": "step"});
162 | }
163 |
164 | function submitCustom() {
165 | var custom_input = document.getElementById("custom-input");
166 | socket.emit("study-event", {"event": "note", "note": custom_input.value});
167 | custom_input.value = "";
168 | }
169 |
170 |
171 | function displaySeconds(time, decimal=3) {
172 | var mins = Math.floor(time / 60);
173 | var secs = time % 60;
174 | if (mins < 10) mins = "0" + mins;
175 | if (secs < 10) secs = "0" + secs.toFixed(decimal);
176 | else secs = secs.toFixed(decimal);
177 | return `${mins}:${secs}`;
178 | }
179 |
180 | socket.on("study-event", (data) => {
181 | // console.log(data)
182 | switch (data.event) {
183 | case "task":
184 | for (task in task_buttons) {
185 | task_buttons[task].classList.remove("selected");
186 | }
187 | task_buttons[data.task].classList.add("selected");
188 | if (data.task == "off" || data.task == "2") {
189 | step_text.firstChild.textContent = "Step N/A";
190 | status_text.innerText = "";
191 | } else {
192 | step_text.firstChild.textContent = "Ready to Start";
193 | next_button.innerText = "Start";
194 | status_text.innerText = "";
195 | }
196 | break;
197 | case "highlight":
198 | step_text.firstChild.textContent = `Step ${data.step + 1}`;
199 | next_button.innerText = "Skip";
200 | status_text.innerText = "In-Progress";
201 | boardviz_text.innerText = data.boardviz ? "On" : "Off";
202 | comp_text.innerText = data.ref;
203 | break;
204 | case "success":
205 | status_text.innerText = "Complete";
206 | next_button.innerText = "Next";
207 | break;
208 | case "timer":
209 | timer_on = data.on;
210 | if (data.on) {
211 | timer_btn.innerText = "Stop";
212 | timer_val = 0;
213 | if (timer_task === null) {
214 | timer_task = window.setInterval(() => {
215 | timer_val += 1 / TIMER_HZ;
216 | timer_text.innerText = displaySeconds(timer_val);
217 | }, 1000 / TIMER_HZ)
218 | }
219 | } else {
220 | timer_btn.innerText = "Start";
221 | clearInterval(timer_task);
222 | timer_task = null;
223 | timer_text.innerText = displaySeconds(data.time);
224 | }
225 | break;
226 | case "settings":
227 | if (data.first_with) {
228 | first_with[data.task].with.classList.add("selected");
229 | first_with[data.task].without.classList.remove("selected");
230 | } else {
231 | first_with[data.task].with.classList.remove("selected");
232 | first_with[data.task].without.classList.add("selected");
233 | }
234 | break;
235 | default:
236 | console.log(data);
237 | break;
238 | }
239 | })
240 |
241 | socket.on("probe-adjust", (data) => {
242 | probe_elements[data.probe].x = data.x;
243 | probe_elements[data.probe].y = data.y;
244 | probe_elements[data.probe].xin.value = data.x.toFixed(1);
245 | probe_elements[data.probe].yin.value = data.y.toFixed(1);
246 | })
247 |
--------------------------------------------------------------------------------
/ardw-app/static/tool-test.js:
--------------------------------------------------------------------------------
1 |
2 | var socket = io();
3 |
4 | window.onload = () => {
5 | document.getElementById("log").addEventListener("click", () => {
6 | socket.emit("tool-debug", { "name": "log" });
7 | });
8 | document.getElementById("ptr-conn").addEventListener("click", () => {
9 | var data = {
10 | "name": "tool-connect",
11 | "type": "ptr",
12 | "val": "device",
13 | "status": "success"
14 | };
15 | socket.emit("tool-debug", data);
16 | });
17 | document.getElementById("ptr-conn-fail").addEventListener("click", () => {
18 | var data = {
19 | "name": "tool-connect",
20 | "type": "ptr",
21 | "val": "device",
22 | "status": "fail"
23 | };
24 | socket.emit("tool-debug", data);
25 | });
26 | document.getElementById("ptr-select").addEventListener("click", () => {
27 | var data = {
28 | "name": "tool-measure",
29 | "type": "ptr",
30 | "coords": {
31 | "x": 100,
32 | "y": 100,
33 | }
34 | };
35 | socket.emit("tool-debug", data);
36 | });
37 | document.getElementById("dmm-conn").addEventListener("click", () => {
38 | var data = {
39 | "name": "tool-connect",
40 | "type": "dmm",
41 | "val": "device",
42 | "status": "success"
43 | };
44 | socket.emit("tool-debug", data);
45 | });
46 | document.getElementById("dmm-red-conn").addEventListener("click", () => {
47 | var data = {
48 | "name": "tool-connect",
49 | "type": "dmm",
50 | "val": "pos",
51 | "status": "success"
52 | };
53 | socket.emit("tool-debug", data);
54 | });
55 | document.getElementById("dmm-black-conn").addEventListener("click", () => {
56 | var data = {
57 | "name": "tool-connect",
58 | "type": "dmm",
59 | "val": "neg",
60 | "status": "success"
61 | };
62 | socket.emit("tool-debug", data);
63 | });
64 | document.getElementById("dmm-conn-fail").addEventListener("click", () => {
65 | var data = {
66 | "name": "tool-connect",
67 | "type": "dmm",
68 | "val": "device",
69 | "status": "fail"
70 | };
71 | socket.emit("tool-debug", data);
72 | });
73 | document.getElementById("dmm-red-conn-fail").addEventListener("click", () => {
74 | var data = {
75 | "name": "tool-connect",
76 | "type": "dmm",
77 | "val": "pos",
78 | "status": "fail"
79 | };
80 | socket.emit("tool-debug", data);
81 | });
82 | document.getElementById("dmm-black-conn-fail").addEventListener("click", () => {
83 | var data = {
84 | "name": "tool-connect",
85 | "type": "dmm",
86 | "val": "neg",
87 | "status": "fail"
88 | };
89 | socket.emit("tool-debug", data);
90 | });
91 | document.getElementById("dmm-measure").addEventListener("click", () => {
92 | var data = {
93 | "name": "measurement",
94 | "measurement": {
95 | "device": "dmm",
96 | "pos": {
97 | "type": "pin",
98 | "val": 148
99 | },
100 | "neg": {
101 | "type": "pin",
102 | "val": 124
103 | },
104 | "unit": "V",
105 | "val": 5.2
106 | }
107 | }
108 | console.log(data);
109 | socket.emit("tool-debug", data);
110 | });
111 | };
112 |
--------------------------------------------------------------------------------
/ardw-app/static/util.js:
--------------------------------------------------------------------------------
1 | // This file is included on all webpages.
2 | // It contains settings, data, and other general global variables,
3 | // as well as a handful of utility functions.
4 |
5 |
6 | /** raw schematic json file */
7 | var schdata;
8 | /** raw layout json file */
9 | var pcbdata;
10 |
11 | /** schid : index in schdata.schematics */
12 | var schid_to_idx = {};
13 | /** ref : refid */
14 | var ref_to_id = {};
15 | /** 'ref.pinnum' : pinidx */
16 | var pinref_to_idx = {};
17 |
18 | /** refid : ref, schids, units={unitnum : schid, bbox, pins=[pinidx]} */
19 | var compdict = {};
20 | /** netname : schids, pins=[pinidx] */
21 | var netdict = {};
22 | /** pinidx : pin = {ref, name, num, pos, schid, net} */
23 | var pindict = [];
24 |
25 | var num_schematics;
26 | var current_schematic; // schid (starts at 1)
27 |
28 | /** A handful of application settings */
29 | var settings = {
30 | "log-error": true,
31 | "log-warning": true,
32 | "find-activate": "key", // 'key', 'auto'
33 | "find-type": "xhair", // 'zoom', 'xhair'
34 | "tool-selection-display": "icon", // 'xhair', 'icon'
35 | };
36 |
37 |
38 | function logerr(msg) {
39 | if (settings["log-error"]) {
40 | console.log("Error: " + msg);
41 | }
42 | }
43 |
44 | function logwarn(msg) {
45 | if (settings["log-warning"]) {
46 | console.log("Warning: " + msg);
47 | }
48 | }
49 |
50 | /**
51 | * Returns the display name for the element, based on type
52 | * @param {*} element Must be {type, val}
53 | */
54 | function getElementName(element) {
55 | if (element === null) {
56 | return "";
57 | }
58 | switch (element.type) {
59 | case "comp":
60 | return `Component ${compdict[element.val].ref}`;
61 | case "pin":
62 | return `Pin ${pindict[element.val].ref}.${pindict[element.val].num}`;
63 | case "net":
64 | return `Net ${element.val}`;
65 | case "deselect":
66 | return "Cancel"
67 | default:
68 | return ""
69 | }
70 | }
71 |
72 | /**
73 | * Forces the given input field to only accept numeric input
74 | * @param {*} input Expects input[type="text"]
75 | */
76 | function forceNumericInput(input) {
77 | input.addEventListener("input", () => {
78 | if (/^-?\d*.?\d*$/.test(input.value)) {
79 | // all good
80 | input.oldValue = input.value;
81 | } else {
82 | if (input.hasOwnProperty("oldValue")) {
83 | input.value = input.oldValue;
84 | } else {
85 | input.value = "";
86 | }
87 | }
88 | });
89 | }
90 |
91 | // ----- From IBOM web/util.js ----- //
92 | var units = {
93 | prefixes: {
94 | giga: ["G", "g", "giga", "Giga", "GIGA"],
95 | mega: ["M", "mega", "Mega", "MEGA"],
96 | kilo: ["K", "k", "kilo", "Kilo", "KILO"],
97 | milli: ["m", "milli", "Milli", "MILLI"],
98 | micro: ["U", "u", "micro", "Micro", "MICRO", "μ", "µ"], // different utf8 μ
99 | nano: ["N", "n", "nano", "Nano", "NANO"],
100 | pico: ["P", "p", "pico", "Pico", "PICO"],
101 | },
102 | unitsShort: ["R", "r", "Ω", "F", "f", "H", "h"],
103 | unitsLong: [
104 | "OHM", "Ohm", "ohm", "ohms",
105 | "FARAD", "Farad", "farad",
106 | "HENRY", "Henry", "henry"
107 | ],
108 | getMultiplier: function (s) {
109 | if (this.prefixes.giga.includes(s)) return 1e9;
110 | if (this.prefixes.mega.includes(s)) return 1e6;
111 | if (this.prefixes.kilo.includes(s)) return 1e3;
112 | if (this.prefixes.milli.includes(s)) return 1e-3;
113 | if (this.prefixes.micro.includes(s)) return 1e-6;
114 | if (this.prefixes.nano.includes(s)) return 1e-9;
115 | if (this.prefixes.pico.includes(s)) return 1e-12;
116 | return 1;
117 | },
118 | valueRegex: null,
119 | }
120 |
121 | function parseValue(val, ref) {
122 | var inferUnit = (unit, ref) => {
123 | if (unit) {
124 | unit = unit.toLowerCase();
125 | if (unit == 'Ω' || unit == "ohm" || unit == "ohms") {
126 | unit = 'r';
127 | }
128 | unit = unit[0];
129 | } else {
130 | ref = /^([a-z]+)\d+$/i.exec(ref);
131 | if (ref) {
132 | ref = ref[1].toLowerCase();
133 | if (ref == "c") unit = 'f';
134 | else if (ref == "l") unit = 'h';
135 | else if (ref == "r" || ref == "rv") unit = 'r';
136 | else unit = null;
137 | }
138 | }
139 | return unit;
140 | };
141 | val = val.replace(/,/g, "");
142 | var match = units.valueRegex.exec(val);
143 | var unit;
144 | if (match) {
145 | val = parseFloat(match[1]);
146 | if (match[2]) {
147 | val = val * units.getMultiplier(match[2]);
148 | }
149 | unit = inferUnit(match[3], ref);
150 | if (!unit) return null;
151 | else return {
152 | val: val,
153 | unit: unit,
154 | extra: match[4],
155 | }
156 | }
157 | match = units.valueAltRegex.exec(val);
158 | if (match && (match[1] || match[4])) {
159 | val = parseFloat(match[1] + "." + match[4]);
160 | if (match[3]) {
161 | val = val * units.getMultiplier(match[3]);
162 | }
163 | unit = inferUnit(match[2], ref);
164 | if (!unit) return null;
165 | else return {
166 | val: val,
167 | unit: unit,
168 | extra: match[5],
169 | }
170 | }
171 | return null;
172 | }
173 |
174 | /** Initializes some IBOM unit parsing (TODO may be unnecessary now) */
175 | function initUtils() {
176 | var allPrefixes = units.prefixes.giga
177 | .concat(units.prefixes.mega)
178 | .concat(units.prefixes.kilo)
179 | .concat(units.prefixes.milli)
180 | .concat(units.prefixes.micro)
181 | .concat(units.prefixes.nano)
182 | .concat(units.prefixes.pico);
183 | var allUnits = units.unitsShort.concat(units.unitsLong);
184 | units.valueRegex = new RegExp("^([0-9\.]+)" +
185 | "\\s*(" + allPrefixes.join("|") + ")?" +
186 | "(" + allUnits.join("|") + ")?" +
187 | "(\\b.*)?$", "");
188 | units.valueAltRegex = new RegExp("^([0-9]*)" +
189 | "(" + units.unitsShort.join("|") + ")?" +
190 | "([GgMmKkUuNnPp])?" +
191 | "([0-9]*)" +
192 | "(\\b.*)?$", "");
193 | for (var bom_type of ["both", "F", "B"]) {
194 | for (var row of pcbdata.bom[bom_type]) {
195 | row.push(parseValue(row[1], row[3][0][0]));
196 | }
197 | }
198 | }
199 | // -------------------- //
200 |
201 | /** Populates the appropriate variables with data from the server,
202 | * from array corresponding to urls ["schdata", "pcbdata", "datadicts"] */
203 | function initData(data) {
204 | schdata = data[0];
205 | pcbdata = data[1];
206 |
207 | schid_to_idx = data[2]["schid_to_idx"]
208 | ref_to_id = data[2]["ref_to_id"]
209 | pinref_to_idx = data[2]["pinref_to_idx"]
210 | compdict = data[2]["compdict"]
211 | netdict = data[2]["netdict"]
212 | pindict = data[2]["pindict"]
213 | }
214 |
215 | // Functions to convert between different bbox formats
216 | // list: [x1, y1, x2, y2]
217 | // obj: {"minx", "miny", "maxx", "maxy"}
218 | // pcbnew: {"pos", "relpos", "angle", "size"}
219 |
220 | /** [x1, y1, x2, y2] => [minx, miny, maxx, maxy] */
221 | function bboxListSort(bbox) {
222 | return [
223 | Math.min(bbox[0], bbox[2]),
224 | Math.min(bbox[1], bbox[3]),
225 | Math.max(bbox[0], bbox[2]),
226 | Math.max(bbox[1], bbox[3])
227 | ];
228 | }
229 | /** [x1, y1, x2, y2] => {"minx", "miny", "maxx", "maxy"} */
230 | function bboxListToObj(bbox) {
231 | return {
232 | "minx": Math.min(bbox[0], bbox[2]),
233 | "miny": Math.min(bbox[1], bbox[3]),
234 | "maxx": Math.max(bbox[0], bbox[2]),
235 | "maxy": Math.max(bbox[1], bbox[3])
236 | };
237 | }
238 | /** {"minx", "miny", "maxx", "maxy"} => [x1, y1, x2, y2] */
239 | function bboxObjToList(bbox) {
240 | return [bbox.minx, bbox.miny, bbox.maxx, bbox.maxy];
241 | }
242 | /** {"pos", "relpos", "angle", "size"} => [x1, y1, x2, y2] */
243 | function bboxPcbnewToList(bbox) {
244 | var corner1;
245 | var corner2;
246 | if (bbox.relpos === undefined) {
247 | // footprint.pad
248 | corner1 = [-bbox.size[0] / 2, -bbox.size[1] / 2];
249 | corner2 = [bbox.size[1] / 2, bbox.size[1] / 2];
250 | } else {
251 | // footprint.bbox
252 | corner1 = [bbox.relpos[0], bbox.relpos[1]];
253 | corner2 = [bbox.relpos[0] + bbox.size[0], bbox.relpos[1] + bbox.size[1]];
254 | corner1 = rotateVector(corner1, bbox.angle);
255 | corner2 = rotateVector(corner2, bbox.angle);
256 | }
257 |
258 | return [
259 | Math.min(corner1[0], corner2[0]) + bbox.pos[0],
260 | Math.min(corner1[1], corner2[1]) + bbox.pos[1],
261 | Math.max(corner1[0], corner2[0]) + bbox.pos[0],
262 | Math.max(corner1[1], corner2[1]) + bbox.pos[1]
263 | ];
264 | }
265 | /** {"pos", "relpos", "angle", "size"} => {"minx", "miny", "maxx", "maxy"} */
266 | function bboxPcbnewToObj(bbox) {
267 | return bboxListToObj(bboxPcbnewToList(bbox));
268 | }
269 |
270 |
271 | /** Converts page offset coords (eg. from event.offsetX) to layout coords*/
272 | function offsetToLayoutCoords(point, layerdict) {
273 | var t = layerdict.transform;
274 | if (layerdict.layer == "B") {
275 | point[0] = (devicePixelRatio * point[0] / t.zoom - t.panx + t.x) / -t.s;
276 | } else {
277 | point[0] = (devicePixelRatio * point[0] / t.zoom - t.panx - t.x) / t.s;
278 | }
279 | point[1] = (devicePixelRatio * point[1] / t.zoom - t.y - t.pany) / t.s;
280 | return rotateVector(point, -ibom_settings.boardRotation);
281 | }
282 | /** Converts layout coords to page client coords (eg. from event.clientX) */
283 | function layoutToClientCoords(point, layer) {
284 | var layerdict = (layer == "F" ? allcanvas.front : allcanvas.back);
285 | var t = layerdict.transform;
286 | var v = rotateVector(point, ibom_settings.boardRotation);
287 | if (layer == "B") {
288 | v[0] = (v[0] * -t.s + t.panx - t.x) * t.zoom / devicePixelRatio;
289 | } else {
290 | v[0] = (v[0] * t.s + t.panx + t.x) * t.zoom / devicePixelRatio;
291 | }
292 | v[1] = (v[1] * t.s + t.pany + t.y) * t.zoom / devicePixelRatio;
293 | var offset_parent = layerdict.bg.offsetParent;
294 | // Last step is converting from offset coords to client coords
295 | return [v[0] + offset_parent.offsetLeft, v[1] + offset_parent.offsetTop]
296 | }
297 |
--------------------------------------------------------------------------------
/ardw-app/static/win7fail.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/ardw-app/static/win7fail.wav
--------------------------------------------------------------------------------
/ardw-app/static/win7success.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/ardw-app/static/win7success.wav
--------------------------------------------------------------------------------
/ardw-app/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AR Debug Workbench Index
6 |
7 |
8 |
9 |
10 |
11 |
12 | Just links to the other pages
13 |
14 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/ardw-app/templates/instrument_panel.html:
--------------------------------------------------------------------------------
1 |
2 |
Instrument Panel Test
3 |
Click button to put DMM into a mode:
4 |
8 |
9 |
Measurement
10 |
11 |
12 |
13 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/ardw-app/templates/projector.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AR Debug Workbench Projector View
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/ardw-app/templates/study.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AR Debug Workbench Study Control Panel
6 |
7 |
8 |
9 |
10 |
11 | Study Control Panel
12 |
13 | Participant
14 |
19 |
24 |
25 | Task
26 |
32 |
33 | Task 1A/B State
34 |
38 |
41 |
45 |
46 | Custom
47 |
51 |
55 |
56 | Manual Probe Adjustment
57 |
60 |
74 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/ardw-app/templates/tool-test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tool Debug Page
6 |
7 |
8 |
9 |
10 | Click links to send server tool events
11 |
12 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/ardw-app/tools.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import time
3 |
4 |
5 | class DebugCard:
6 | def __init__(self, device, pos, neg, unit=None, val=None, lo=None, hi=None) -> None:
7 | self.device: str = device
8 | self.pos: dict = pos
9 | self.neg: dict = neg
10 | self.unit: str = unit
11 | self.val: float = val
12 | self.lo: float = lo
13 | self.hi: float = hi
14 | if unit is None:
15 | self.lo = None
16 | self.hi = None
17 |
18 | def __repr__(self) -> str:
19 | return str(self.__dict__)
20 |
21 | # returns true iff self is a valid measurement update for target
22 | def will_update(self, target) -> bool:
23 | if target.is_complete():
24 | return False
25 | return (
26 | self.device == target.device and
27 | self.pos == target.pos and
28 | self.neg == target.neg and
29 | (target.unit is None or self.unit == target.unit)
30 | )
31 |
32 | def is_complete(self) -> bool:
33 | return self.val is not None
34 |
35 | # 0 is inbounds, -1 is too low, +1 is too high, None is no bounds/val
36 | def inbounds(self) -> int:
37 | if self.val is None or (self.lo is None and self.hi is None):
38 | return None
39 | elif self.lo is not None and self.val < self.lo:
40 | return -1
41 | elif self.hi is not None and self.val > self.hi:
42 | return 1
43 | else:
44 | return 0
45 |
46 | def to_dict(self) -> dict:
47 | return self.__dict__
48 |
49 |
50 | class DebugSession:
51 | def __init__(self, name="", notes="") -> None:
52 | self.name: str = name
53 | self.notes: str = notes
54 | self.timestamp: str = time.strftime("%H:%M:%S", time.localtime())
55 | self.cards: list[DebugCard] = []
56 | self.next: int = -1
57 |
58 | def __repr__(self) -> str:
59 | return str(self.__dict__)
60 |
61 | def add_card(self, card: DebugCard) -> int:
62 | i = len(self.cards)
63 | self.cards.append(card)
64 | if not card.is_complete() and self.next == -1:
65 | self.next = i
66 | return i
67 |
68 | def remove_card(self, i) -> DebugCard:
69 | if self.next == i:
70 | self.__update_next(i + 1)
71 | return self.cards.pop(i)
72 |
73 | def get_next(self):
74 | if self.next == -1:
75 | return -1, None
76 | else:
77 | return self.next, self.cards[self.next]
78 |
79 | # returns the resulting card, the id of the card, and update flag
80 | def measure(self, device, pos, neg, unit, val):
81 | measure_card = DebugCard(device, pos, neg, unit, val)
82 | # check if we have a card for this measurement
83 | for i, card in enumerate(self.cards):
84 | if measure_card.will_update(card):
85 | card.unit = measure_card.unit
86 | card.val = measure_card.val
87 | if self.next == i:
88 | self.__update_next(i + 1)
89 | return card, i, True
90 |
91 | # didn't have a matching card, so just append to end of deck
92 | self.cards.append(measure_card)
93 | return measure_card, len(self.cards) - 1, False
94 |
95 | def export(self) -> None:
96 | logging.info("Export WIP")
97 | logging.info(str(self))
98 |
99 | def to_dict(self) -> dict:
100 | output = self.__dict__.copy()
101 | output["cards"] = []
102 | for card in self.cards:
103 | output["cards"].append(card.__dict__)
104 | return output
105 |
106 | def __update_next(self, start=0):
107 | self.next = -1
108 | for i in range(start, len(self.cards)):
109 | if not self.cards[i].is_complete():
110 | self.next = i
111 | break
112 |
--------------------------------------------------------------------------------
/ardw-plugin/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | # Registers the plugin for KiCad
3 | from .ardw_action import ARDebugWorkbenchPlugin
4 | ARDebugWorkbenchPlugin().register()
5 |
--------------------------------------------------------------------------------
/ardw-plugin/__main__.py:
--------------------------------------------------------------------------------
1 |
2 | # TODO this is for the command line version of plugin
3 |
--------------------------------------------------------------------------------
/ardw-plugin/ardw_action.py:
--------------------------------------------------------------------------------
1 |
2 | import json
3 | import logging
4 | import os
5 | import sys
6 | import time
7 | import traceback
8 |
9 | import pcbnew
10 | from .ibom.pcbparser import PcbnewParser, generate_bom, round_floats
11 | from .ibom.pcbparser import Config
12 | from .core import parseProject, fix_file
13 |
14 | class ARDebugWorkbenchPlugin(pcbnew.ActionPlugin, object):
15 |
16 | def defaults(self):
17 | self.name = "AR Debug Workbench Plugin"
18 | self.category = "Read PCB"
19 | self.pcbnew_icon_support = hasattr(self, "show_toolbar_button")
20 | self.show_toolbar_button = True
21 | self.icon_file_name = os.path.join(os.path.dirname(__file__), 'icon.png')
22 | self.description = "Extracts pcbnew data for AR Debug Workbench"
23 |
24 | def Run(self):
25 |
26 | plugin_dir = os.path.dirname(os.path.realpath(__file__))
27 | log_file = os.path.join(plugin_dir, "ardw_plugin.log")
28 |
29 | logging.basicConfig(level=logging.DEBUG,
30 | filename=log_file,
31 | filemode='w',
32 | format='%(asctime)s %(name)s %(lineno)d:%(message)s',
33 | datefmt='%m-%d %H:%M:%S')
34 | logger = logging.getLogger("ARDW Plugin")
35 |
36 | logger.info("Plugin executed on: " + repr(sys.platform))
37 | logger.info("Plugin executed with python version: " + repr(sys.version))
38 |
39 | try:
40 | config = Config("test")
41 |
42 | board = pcbnew.GetBoard()
43 | pcb_file_path = board.GetFileName()
44 | parser = PcbnewParser(pcb_file_path, config, logger, board)
45 |
46 | project_dir = os.path.dirname(os.path.realpath(pcb_file_path))
47 | output_dir = os.path.join(project_dir, "ardw")
48 | try:
49 | os.makedirs(output_dir)
50 | except OSError:
51 | if not os.path.isdir(output_dir):
52 | raise
53 |
54 | sch_json_path = os.path.join(output_dir, "schdata.json")
55 | parseProject(logger, pcb_file_path, sch_json_path)
56 |
57 | logger.info("Parsing pcbdata")
58 | pcbdata, components = parser.parse()
59 | if not pcbdata and not components:
60 | logger.error("Failed to parse PCB data")
61 |
62 | logger.info("Generating bom data")
63 | pcbdata["bom"] = generate_bom(components, config)
64 | pcbdata_str = json.dumps(round_floats(pcbdata, 6))
65 |
66 |
67 | pcb_json_path = os.path.join(output_dir, "pcbdata.json")
68 | logger.info("Writing result to {}".format(pcb_json_path))
69 |
70 | with open(pcb_json_path, "w") as json_file:
71 | json_file.write(pcbdata_str)
72 |
73 | logger.info("Checking for svgs and fixing")
74 | for filename in os.listdir(output_dir):
75 | if filename.endswith(".svg"):
76 | filepath = os.path.join(output_dir, filename)
77 | logger.info("Fixing {}".format(filepath))
78 | fix_file(filepath)
79 |
80 | logger.info("Done")
81 |
82 | except Exception as _:
83 | logger.error(traceback.format_exc())
84 |
85 |
86 |
--------------------------------------------------------------------------------
/ardw-plugin/clear_pyc.sh:
--------------------------------------------------------------------------------
1 | find . -name "*.pyc" -type f -delete
2 | rm ardw_plugin.log
3 |
--------------------------------------------------------------------------------
/ardw-plugin/core/__init__.py:
--------------------------------------------------------------------------------
1 | # Takes the board file path from pcbnew and figures out
2 | # where the .sch, .lib, and .net files are for sch_reader.py
3 |
4 | import os
5 |
6 | from .sch_reader import parseFiles, outputJSON
7 | from .svg_fix import fix_file
8 |
9 | # Takes the path to the project's .kicad_pcb file
10 | # from pcbnew.GetBoard().GetFileName()
11 | # libpaths holds real paths to any external lib files
12 | """
13 | Given a board file, figures out where the important files are
14 | and runs parseFiles(), writing the result to output_path
15 | Inputs: logger -- logger to write errors, etc
16 | pcb_file_path -- real path to board, from pcbnew.py
17 | output_path -- real path to output file
18 | extlibpaths -- list of real paths to lib files not in the main directory
19 | Output: none
20 | """
21 | def parseProject(logger, pcb_file_path, output_path, extlibpaths=[]):
22 |
23 | project_dir = os.path.dirname(os.path.realpath(pcb_file_path))
24 |
25 | project_name = os.path.split(pcb_file_path)[1][:-10]
26 |
27 | schfiles = []
28 | libfiles = []
29 | for filename in os.listdir(project_dir):
30 | if filename.endswith(".sch"):
31 | schname = str(filename[:-4])
32 | if schname != project_name:
33 | schfiles.append(schname)
34 | elif filename.endswith(".lib"):
35 | libname = str(filename[:-4])
36 | if libname != (project_name + "-cache"):
37 | libfiles.append(libname)
38 |
39 | logger.info("Parsing '{}' at {}".format(project_name, project_dir))
40 | schlist, netlist = parseFiles(project_dir, project_name, schfiles, libfiles)
41 | logger.info("Writing result to {}".format(output_path))
42 | outputJSON(output_path, schlist, netlist)
43 |
--------------------------------------------------------------------------------
/ardw-plugin/core/svg_fix.py:
--------------------------------------------------------------------------------
1 | # Changes svg width/height to be proportional to their viewbox
2 | # This is necessary for the scaling and zooming of the schematic to work
3 |
4 | import os
5 | import re
6 | import sys
7 |
8 | def fix_file(svgpath, outpath=None):
9 | if not os.path.isfile(svgpath):
10 | print("Error: file not found at {}".format(svgpath))
11 | return
12 |
13 | with open(svgpath, "r") as file:
14 | data = file.readlines()
15 |
16 | for linenum in range(len(data)):
17 | line = data[linenum]
18 | matchpattern = 'width="(.*)" height="(.*)" viewBox=" *([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*) *"'
19 | match = re.search(matchpattern, line)
20 | if not match is None:
21 | print("Match found in line {}".format(linenum))
22 | width = int(match.groups()[4]) // 10
23 | height = int(match.groups()[5]) // 10
24 | replacepattern = 'width="{}" height="{}" viewBox="\g<3> \g<4> \g<5> \g<6>"'.format(width, height)
25 | data[linenum] = re.sub(matchpattern, replacepattern, line)
26 | break
27 |
28 | if outpath is None:
29 | outpath = svgpath
30 | with open(outpath, "w") as file:
31 | file.writelines(data)
32 |
33 |
34 | if __name__ == "__main__":
35 | if len(sys.argv) < 2:
36 | sys.exit("Usage: python svg_fix.py ")
37 |
38 | svgpath = sys.argv[1]
39 |
40 | fix_file(svgpath)
41 |
42 |
43 |
--------------------------------------------------------------------------------
/ardw-plugin/ibom/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/ardw-plugin/ibom/__init__.py
--------------------------------------------------------------------------------
/ardw-plugin/ibom/common.py:
--------------------------------------------------------------------------------
1 | # Taken as is from Interactive HTML BOM
2 | # https://github.com/openscopeproject/InteractiveHtmlBom
3 |
4 | import math
5 |
6 | from .svgpath import parse_path
7 |
8 |
9 | class EcadParser(object):
10 |
11 | def __init__(self, file_name, config, logger):
12 | """
13 | :param file_name: path to file that should be parsed.
14 | :param config: Config instance
15 | :param logger: logging object.
16 | """
17 | self.file_name = file_name
18 | self.config = config
19 | self.logger = logger
20 | self.extra_data_func = lambda f, b: ([], {})
21 |
22 | def parse(self):
23 | """
24 | Abstract method that should be overridden in implementations.
25 | Performs all the parsing and returns a tuple of
26 | (pcbdata, components)
27 | pcbdata is described in DATAFORMAT.md
28 | components is list of Component objects
29 | :return:
30 | """
31 | pass
32 |
33 | def latest_extra_data(self, extra_dirs=None):
34 | """
35 | Abstract method that may be overridden in implementations that support
36 | extra field data.
37 | :param extra_dirs: List of extra directories to search.
38 | :return: File name of most recent file with extra field data.
39 | """
40 | return None
41 |
42 | def add_drawing_bounding_box(self, drawing, bbox):
43 | # type: (dict, BoundingBox) -> None
44 |
45 | def add_segment():
46 | bbox.add_segment(drawing['start'][0], drawing['start'][1],
47 | drawing['end'][0], drawing['end'][1],
48 | drawing['width'] / 2)
49 |
50 | def add_circle():
51 | bbox.add_circle(drawing['start'][0], drawing['start'][1],
52 | drawing['radius'] + drawing['width'] / 2)
53 |
54 | def add_svgpath():
55 | width = drawing.get('width', 0)
56 | bbox.add_svgpath(drawing['svgpath'], width, self.logger)
57 |
58 | def add_polygon():
59 | if 'polygons' not in drawing:
60 | add_svgpath()
61 | return
62 | polygon = drawing['polygons'][0]
63 | for point in polygon:
64 | bbox.add_point(point[0], point[1])
65 |
66 | {
67 | 'segment': add_segment,
68 | 'circle': add_circle,
69 | 'arc': add_svgpath,
70 | 'polygon': add_polygon,
71 | 'text': lambda: None, # text is not really needed for bounding box
72 | }.get(drawing['type'])()
73 |
74 |
75 | class Component(object):
76 | """Simple data object to store component data needed for bom table."""
77 |
78 | def __init__(self, ref, val, footprint, layer, attr=None, extra_fields={}):
79 | self.ref = ref
80 | self.val = val
81 | self.footprint = footprint
82 | self.layer = layer
83 | self.attr = attr
84 | self.extra_fields = extra_fields
85 |
86 |
87 | class BoundingBox(object):
88 | """Geometry util to calculate and compound bounding box of simple shapes."""
89 |
90 | def __init__(self):
91 | self._x0 = None
92 | self._y0 = None
93 | self._x1 = None
94 | self._y1 = None
95 |
96 | def to_dict(self):
97 | # type: () -> dict
98 | return {
99 | "minx": self._x0,
100 | "miny": self._y0,
101 | "maxx": self._x1,
102 | "maxy": self._y1,
103 | }
104 |
105 | def to_component_dict(self):
106 | # type: () -> dict
107 | return {
108 | "pos": [self._x0, self._y0],
109 | "relpos": [0, 0],
110 | "size": [self._x1 - self._x0, self._y1 - self._y0],
111 | "angle": 0,
112 | }
113 |
114 | def add(self, other):
115 | """Add another bounding box.
116 | :type other: BoundingBox
117 | """
118 | if other._x0 is not None:
119 | self.add_point(other._x0, other._y0)
120 | self.add_point(other._x1, other._y1)
121 | return self
122 |
123 | @staticmethod
124 | def _rotate(x, y, rx, ry, angle):
125 | sin = math.sin(math.radians(angle))
126 | cos = math.cos(math.radians(angle))
127 | new_x = rx + (x - rx) * cos - (y - ry) * sin
128 | new_y = ry + (x - rx) * sin + (y - ry) * cos
129 | return new_x, new_y
130 |
131 | def add_point(self, x, y, rx=0, ry=0, angle=0):
132 | x, y = self._rotate(x, y, rx, ry, angle)
133 | if self._x0 is None:
134 | self._x0 = x
135 | self._y0 = y
136 | self._x1 = x
137 | self._y1 = y
138 | else:
139 | self._x0 = min(self._x0, x)
140 | self._y0 = min(self._y0, y)
141 | self._x1 = max(self._x1, x)
142 | self._y1 = max(self._y1, y)
143 | return self
144 |
145 | def add_segment(self, x0, y0, x1, y1, r):
146 | self.add_circle(x0, y0, r)
147 | self.add_circle(x1, y1, r)
148 | return self
149 |
150 | def add_rectangle(self, x, y, w, h, angle=0):
151 | self.add_point(x - w / 2, y - h / 2, x, y, angle)
152 | self.add_point(x + w / 2, y - h / 2, x, y, angle)
153 | self.add_point(x - w / 2, y + h / 2, x, y, angle)
154 | self.add_point(x + w / 2, y + h / 2, x, y, angle)
155 | return self
156 |
157 | def add_circle(self, x, y, r):
158 | self.add_point(x - r, y)
159 | self.add_point(x, y - r)
160 | self.add_point(x + r, y)
161 | self.add_point(x, y + r)
162 | return self
163 |
164 | def add_svgpath(self, svgpath, width, logger):
165 | w = width / 2
166 | for segment in parse_path(svgpath, logger):
167 | x0, x1, y0, y1 = segment.bbox()
168 | self.add_point(x0 - w, y0 - w)
169 | self.add_point(x1 + w, y1 + w)
170 |
171 | def pad(self, amount):
172 | """Add small padding to the box."""
173 | if self._x0 is not None:
174 | self._x0 -= amount
175 | self._y0 -= amount
176 | self._x1 += amount
177 | self._y1 += amount
178 |
179 | def initialized(self):
180 | return self._x0 is not None
181 |
--------------------------------------------------------------------------------
/ardw-plugin/ibom/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/ardw-plugin/ibom/core/__init__.py
--------------------------------------------------------------------------------
/ardw-plugin/ibom/core/fontparser.py:
--------------------------------------------------------------------------------
1 | # Taken as is from Interactive HTML BOM
2 | # https://github.com/openscopeproject/InteractiveHtmlBom
3 |
4 | from .newstroke_font import NEWSTROKE_FONT
5 |
6 |
7 | class FontParser:
8 | STROKE_FONT_SCALE = 1.0 / 21.0
9 | FONT_OFFSET = -10
10 |
11 | def __init__(self):
12 | self.parsed_font = {}
13 |
14 | def parse_font_char(self, chr):
15 | lines = []
16 | line = []
17 | glyph_x = 0
18 | index = ord(chr) - ord(' ')
19 | if index >= len(NEWSTROKE_FONT):
20 | index = ord('?') - ord(' ')
21 | glyph_str = NEWSTROKE_FONT[index]
22 | for i in range(0, len(glyph_str), 2):
23 | coord = glyph_str[i:i + 2]
24 |
25 | # The first two values contain the width of the char
26 | if i < 2:
27 | glyph_x = (ord(coord[0]) - ord('R')) * self.STROKE_FONT_SCALE
28 | glyph_width = (ord(coord[1]) - ord(coord[0])) * self.STROKE_FONT_SCALE
29 | elif coord[0] == ' ' and coord[1] == 'R':
30 | lines.append(line)
31 | line = []
32 | else:
33 | line.append([
34 | (ord(coord[0]) - ord('R')) * self.STROKE_FONT_SCALE - glyph_x,
35 | (ord(coord[1]) - ord('R') + self.FONT_OFFSET) * self.STROKE_FONT_SCALE
36 | ])
37 |
38 | if len(line) > 0:
39 | lines.append(line)
40 |
41 | return {
42 | 'w': glyph_width,
43 | 'l': lines
44 | }
45 |
46 | def parse_font_for_string(self, s):
47 | for c in s:
48 | if c == '\t' and ' ' not in self.parsed_font:
49 | # tabs rely on space char to calculate offset
50 | self.parsed_font[' '] = self.parse_font_char(' ')
51 | if c not in self.parsed_font and ord(c) >= ord(' '):
52 | self.parsed_font[c] = self.parse_font_char(c)
53 |
54 | def get_parsed_font(self):
55 | return self.parsed_font
56 |
--------------------------------------------------------------------------------
/ardw-plugin/ibom/core/units.py:
--------------------------------------------------------------------------------
1 | # _*_ coding:utf-8 _*_
2 |
3 | # Stolen from https://github.com/SchrodingersGat/KiBoM/blob/master/KiBOM/units.py
4 |
5 | """
6 |
7 | This file contains a set of functions for matching values which may be written
8 | in different formats e.g.
9 | 0.1uF = 100n (different suffix specified, one has missing unit)
10 | 0R1 = 0.1Ohm (Unit replaces decimal, different units)
11 |
12 | """
13 |
14 | import re
15 | import locale
16 |
17 | current_locale = locale.setlocale(locale.LC_NUMERIC)
18 | try:
19 | locale.setlocale(locale.LC_NUMERIC, '')
20 | except Exception as ignore:
21 | # sometimes setlocale with empty string doesn't work on OSX
22 | pass
23 | decimal_separator = locale.localeconv()['decimal_point']
24 | locale.setlocale(locale.LC_NUMERIC, current_locale)
25 |
26 | PREFIX_MICRO = [u"μ", "u", "micro"]
27 | PREFIX_MILLI = ["milli", "m"]
28 | PREFIX_NANO = ["nano", "n"]
29 | PREFIX_PICO = ["pico", "p"]
30 | PREFIX_KILO = ["kilo", "k"]
31 | PREFIX_MEGA = ["mega", "meg"]
32 | PREFIX_GIGA = ["giga", "g"]
33 |
34 | # All prefices
35 | PREFIX_ALL = PREFIX_PICO + PREFIX_NANO + PREFIX_MICRO + \
36 | PREFIX_MILLI + PREFIX_KILO + PREFIX_MEGA + PREFIX_GIGA
37 |
38 | # Common methods of expressing component units
39 | UNIT_R = ["r", "ohms", "ohm", u"Ω"]
40 | UNIT_C = ["farad", "f"]
41 | UNIT_L = ["henry", "h"]
42 |
43 | UNIT_ALL = UNIT_R + UNIT_C + UNIT_L
44 |
45 | """
46 | Return a simplified version of a units string, for comparison purposes
47 | """
48 |
49 |
50 | def getUnit(unit):
51 | if not unit:
52 | return None
53 |
54 | unit = unit.lower()
55 |
56 | if unit in UNIT_R:
57 | return "R"
58 | if unit in UNIT_C:
59 | return "F"
60 | if unit in UNIT_L:
61 | return "H"
62 |
63 | return None
64 |
65 |
66 | """
67 | Return the (numerical) value of a given prefix
68 | """
69 |
70 |
71 | def getPrefix(prefix):
72 | if not prefix:
73 | return 1
74 |
75 | prefix = prefix.lower()
76 |
77 | if prefix in PREFIX_PICO:
78 | return 1.0e-12
79 | if prefix in PREFIX_NANO:
80 | return 1.0e-9
81 | if prefix in PREFIX_MICRO:
82 | return 1.0e-6
83 | if prefix in PREFIX_MILLI:
84 | return 1.0e-3
85 | if prefix in PREFIX_KILO:
86 | return 1.0e3
87 | if prefix in PREFIX_MEGA:
88 | return 1.0e6
89 | if prefix in PREFIX_GIGA:
90 | return 1.0e9
91 |
92 | return 1
93 |
94 |
95 | def groupString(group): # return a reg-ex string for a list of values
96 | return "|".join(group)
97 |
98 |
99 | def matchString():
100 | return "^([0-9\.]+)(" + groupString(PREFIX_ALL) + ")*(" + groupString(
101 | UNIT_ALL) + ")*(\d*)$"
102 |
103 |
104 | """
105 | Return a normalized value and units for a given component value string
106 | e.g. compMatch("10R2") returns (10, R)
107 | e.g. compMatch("3.3mOhm") returns (0.0033, R)
108 | """
109 |
110 |
111 | def compMatch(component):
112 | component = component.strip().lower()
113 | if decimal_separator == ',':
114 | # replace separator with dot
115 | component = component.replace(",", ".")
116 | else:
117 | # remove thousands separator
118 | component = component.replace(",", "")
119 | match = matchString()
120 | result = re.search(match, component)
121 |
122 | if not result:
123 | return None
124 |
125 | if not len(result.groups()) == 4:
126 | return None
127 |
128 | value, prefix, units, post = result.groups()
129 |
130 | # special case where units is in the middle of the string
131 | # e.g. "0R05" for 0.05Ohm
132 | # in this case, we will NOT have a decimal
133 | # we will also have a trailing number
134 |
135 | if post and "." not in value:
136 | try:
137 | value = float(int(value))
138 | postValue = float(int(post)) / (10 ** len(post))
139 | value = value * 1.0 + postValue
140 | except:
141 | return None
142 |
143 | try:
144 | val = float(value)
145 | except:
146 | return None
147 |
148 | val = "{0:.15f}".format(val * 1.0 * getPrefix(prefix))
149 |
150 | return (val, getUnit(units))
151 |
152 |
153 | def componentValue(valString):
154 | result = compMatch(valString)
155 |
156 | if not result:
157 | return valString, None # return the same string back with `None` unit
158 |
159 | if not len(result) == 2: # result length is incorrect
160 | return valString, None # return the same string back with `None` unit
161 |
162 | return result # (val,unit)
163 |
164 |
165 | # compare two values
166 |
167 |
168 | def compareValues(c1, c2):
169 | r1 = compMatch(c1)
170 | r2 = compMatch(c2)
171 |
172 | if not r1 or not r2:
173 | return False
174 |
175 | (v1, u1) = r1
176 | (v2, u2) = r2
177 |
178 | if v1 == v2:
179 | # values match
180 | if u1 == u2:
181 | return True # units match
182 | if not u1:
183 | return True # no units for component 1
184 | if not u2:
185 | return True # no units for component 2
186 |
187 | return False
188 |
--------------------------------------------------------------------------------
/ardw-plugin/ibom/kicad_extra/__init__.py:
--------------------------------------------------------------------------------
1 | # Taken as is from Interactive HTML BOM
2 | # https://github.com/openscopeproject/InteractiveHtmlBom
3 |
4 | import os
5 |
6 | from .xmlparser import XmlParser
7 | from .netlistparser import NetlistParser
8 |
9 | PARSERS = {
10 | '.xml': XmlParser,
11 | '.net': NetlistParser
12 | }
13 |
14 |
15 | def parse_schematic_data(file_name, normalize_case):
16 | if not os.path.isfile(file_name):
17 | return None
18 | extension = os.path.splitext(file_name)[1]
19 | if extension not in PARSERS:
20 | return None
21 | else:
22 | parser = PARSERS[extension](file_name)
23 | return parser.parse(normalize_case)
24 |
25 |
26 | def find_latest_schematic_data(base_name, directories):
27 | """
28 | :param base_name: base name of pcb file
29 | :param directories: list of directories to search
30 | :return: last modified parsable file path or None if not found
31 | """
32 | files = []
33 | for d in directories:
34 | files.extend(_find_in_dir(d))
35 | # sort by decreasing modification time
36 | files = sorted(files, reverse=True)
37 | if files:
38 | # try to find first (last modified) file that has name matching pcb file
39 | for _, f in files:
40 | if os.path.splitext(os.path.basename(f))[0] == base_name:
41 | return f
42 | # if no such file is found just return last modified
43 | return files[0][1]
44 | else:
45 | return None
46 |
47 |
48 | def _find_in_dir(dir):
49 | _, _, files = next(os.walk(dir), (None, None, []))
50 | # filter out files that we can not parse
51 | files = [f for f in files if os.path.splitext(f)[1] in PARSERS.keys()]
52 | files = [os.path.join(dir, f) for f in files]
53 | # get their modification time and sort in descending order
54 | return [(os.path.getmtime(f), f) for f in files]
55 |
--------------------------------------------------------------------------------
/ardw-plugin/ibom/kicad_extra/netlistparser.py:
--------------------------------------------------------------------------------
1 | # Taken as is from Interactive HTML BOM
2 | # https://github.com/openscopeproject/InteractiveHtmlBom
3 |
4 | import io
5 |
6 | from .parser_base import ParserBase
7 | from .sexpressions import parse_sexpression
8 |
9 |
10 | class NetlistParser(ParserBase):
11 | def get_extra_field_data(self):
12 | with io.open(self.file_name, 'r', encoding='utf-8') as f:
13 | sexpression = parse_sexpression(f.read())
14 | components = None
15 | for s in sexpression:
16 | if s[0] == 'components':
17 | components = s[1:]
18 | if components is None:
19 | return None
20 | field_set = set()
21 | comp_dict = {}
22 | for c in components:
23 | ref = None
24 | fields = None
25 | datasheet = None
26 | libsource = None
27 | for f in c[1:]:
28 | if f[0] == 'ref':
29 | ref = f[1]
30 | if f[0] == 'fields':
31 | fields = f[1:]
32 | if f[0] == 'datasheet':
33 | datasheet = f[1]
34 | if f[0] == 'libsource':
35 | libsource = f[1:]
36 | if ref is None:
37 | return None
38 | ref_fields = comp_dict.setdefault(ref, {})
39 | if datasheet and datasheet != '~':
40 | field_set.add('Datasheet')
41 | ref_fields['Datasheet'] = datasheet
42 | if libsource is not None:
43 | for lib_field in libsource:
44 | if lib_field[0] == 'description':
45 | field_set.add('Description')
46 | ref_fields['Description'] = lib_field[1]
47 | if fields is None:
48 | continue
49 | for f in fields:
50 | if len(f) > 1:
51 | field_set.add(f[1][1])
52 | if len(f) > 2:
53 | ref_fields[f[1][1]] = f[2]
54 |
55 | return list(field_set), comp_dict
56 |
--------------------------------------------------------------------------------
/ardw-plugin/ibom/kicad_extra/parser_base.py:
--------------------------------------------------------------------------------
1 | # Taken as is from Interactive HTML BOM
2 | # https://github.com/openscopeproject/InteractiveHtmlBom
3 |
4 | class ParserBase:
5 |
6 | def __init__(self, file_name):
7 | """
8 | :param file_name: path to file that should be parsed.
9 | """
10 | self.file_name = file_name
11 |
12 | @staticmethod
13 | def normalize_field_names(data):
14 | field_map = {f.lower(): f for f in reversed(data[0])}
15 |
16 | def remap(ref_fields):
17 | return {field_map[f.lower()]: v for (f, v) in
18 | sorted(ref_fields.items(), reverse=True)}
19 |
20 | field_data = {r: remap(d) for (r, d) in data[1].items()}
21 | return field_map.values(), field_data
22 |
23 | def parse(self, normalize_case):
24 | data = self.get_extra_field_data()
25 | if data is None:
26 | return None
27 | if normalize_case:
28 | data = self.normalize_field_names(data)
29 | return sorted(data[0]), data[1]
30 |
31 | def get_extra_field_data(self):
32 | # type: () -> tuple
33 | """
34 | Parses the file and returns extra field data.
35 | :return: tuple of the format
36 | (
37 | [field_name1, field_name2,... ],
38 | {
39 | ref1: {
40 | field_name1: field_value1,
41 | field_name2: field_value2,
42 | ...
43 | ],
44 | ref2: ...
45 | }
46 | )
47 | """
48 | pass
49 |
--------------------------------------------------------------------------------
/ardw-plugin/ibom/kicad_extra/sexpressions.py:
--------------------------------------------------------------------------------
1 | # Taken as is from Interactive HTML BOM
2 | # https://github.com/openscopeproject/InteractiveHtmlBom
3 |
4 | import re
5 |
6 | term_regex = r'''(?mx)
7 | \s*(?:
8 | (?P\()|
9 | (?P\))|
10 | (?P"(?:\\\\|\\"|[^"])*")|
11 | (?P[^(^)\s]+)
12 | )'''
13 | pattern = re.compile(term_regex)
14 |
15 |
16 | def parse_sexpression(sexpression):
17 | stack = []
18 | out = []
19 | for terms in pattern.finditer(sexpression):
20 | term, value = [(t, v) for t, v in terms.groupdict().items() if v][0]
21 | if term == 'open':
22 | stack.append(out)
23 | out = []
24 | elif term == 'close':
25 | assert stack, "Trouble with nesting of brackets"
26 | tmp, out = out, stack.pop(-1)
27 | out.append(tmp)
28 | elif term == 'sq':
29 | out.append(value[1:-1].replace('\\\\', '\\').replace('\\"', '"'))
30 | elif term == 's':
31 | out.append(value)
32 | else:
33 | raise NotImplementedError("Error: %s, %s" % (term, value))
34 | assert not stack, "Trouble with nesting of brackets"
35 | return out[0]
36 |
--------------------------------------------------------------------------------
/ardw-plugin/ibom/kicad_extra/xmlparser.py:
--------------------------------------------------------------------------------
1 | # Taken as is from Interactive HTML BOM
2 | # https://github.com/openscopeproject/InteractiveHtmlBom
3 |
4 | from xml.dom import minidom
5 |
6 | from .parser_base import ParserBase
7 |
8 |
9 | class XmlParser(ParserBase):
10 | @staticmethod
11 | def get_text(nodelist):
12 | rc = []
13 | for node in nodelist:
14 | if node.nodeType == node.TEXT_NODE:
15 | rc.append(node.data)
16 | return ''.join(rc)
17 |
18 | def get_extra_field_data(self):
19 | xml = minidom.parse(self.file_name)
20 | components = xml.getElementsByTagName('comp')
21 | field_set = set()
22 | comp_dict = {}
23 | for c in components:
24 | ref_fields = comp_dict.setdefault(c.attributes['ref'].value, {})
25 | datasheet = c.getElementsByTagName('datasheet')
26 | if datasheet:
27 | datasheet = self.get_text(datasheet[0].childNodes)
28 | if datasheet != '~':
29 | field_set.add('Datasheet')
30 | ref_fields['Datasheet'] = datasheet
31 | libsource = c.getElementsByTagName('libsource')
32 | if libsource and libsource[0].hasAttribute('description'):
33 | field_set.add('Description')
34 | attr = libsource[0].attributes['description']
35 | ref_fields['Description'] = attr.value
36 | for f in c.getElementsByTagName('field'):
37 | name = f.attributes['name'].value
38 | field_set.add(name)
39 | ref_fields[name] = self.get_text(f.childNodes)
40 |
41 | return list(field_set), comp_dict
42 |
--------------------------------------------------------------------------------
/ardw-plugin/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/ardw-plugin/icon.png
--------------------------------------------------------------------------------
/cv/projector_calibration.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import cv2 as cv
3 | import math
4 |
5 | #size of projector frame
6 | frameSize = [1920, 1080]
7 | # size of checkerboard squares to project
8 | gridSize = [4, 3]
9 | numCaptures = (gridSize[0]+1) * (gridSize[1]+1)
10 |
11 | # world points in homogeneous coordinates
12 | """
13 | [X_1 X_2 X_3 ... X_n]
14 | [Y_1 Y_2 Y_3 ... Y_n]
15 | [Z_1 Z_2 Z_3 ... Z_n]
16 | [ 1 1 1 ... 1 ]
17 | """
18 | capturedWorldPoints = np.ones((4, numCaptures))
19 |
20 | #image points in homogeneous coordinates
21 | """
22 | [u_1 u_2 u_3 ... u_n]
23 | [v_1 v_2 v_3 ... v_n]
24 | [ 1 1 1 ... 1 ]
25 | """
26 | imagePoints = np.ones((3, numCaptures))
27 |
28 | #Projection matrix
29 | projectionMatrix = np.empty((3, 4))
30 |
31 |
32 |
33 | checkerBoard = np.zeros((frameSize[1], frameSize[0]))
34 |
35 | def makeCheckerboard(gridSize, frameSize):
36 | if frameSize[0] % gridSize[0] != 0:
37 | print("Frame width %i is not a multiple of checkerboard width %i. Use exact divsor for maximum precision." % (frameSize[0], gridSize[0]))
38 |
39 | if frameSize[1] % gridSize[1] != 0:
40 | print("Frame height %i is not a multiple of checkerboard height %i. Use exact divsor for maximum precision." % (frameSize[1], gridSize[1]))
41 |
42 | for row in range(0, np.shape(checkerBoard)[0]):
43 | for col in range(0, np.shape(checkerBoard)[1]):
44 | if ((math.floor(row / (frameSize[1] / gridSize[1]))) + (math.floor(col / (frameSize[0] / gridSize[0])))) % 2 == 0:
45 | checkerBoard[row, col] = 1
46 |
47 | cv.imshow("image", checkerBoard)
48 | cv.waitKey(0)
49 | cv.destroyAllWindows()
50 |
51 | def pointGuidance(gridSize, checkerBoard, imagePoints, capturedWorldPoints):
52 |
53 | thickeness = 2
54 | radius = 20
55 | circleColor = (1,0,0)
56 |
57 | text = "Please place probe tip accurately on corner circled in blue. Press any key to capture position"
58 | textColor = (0.5,0.5,0.5)
59 |
60 | for row in range(0, gridSize[0]+1):
61 | for col in range(0, gridSize[1]+1):
62 | center = ( row*(frameSize[0] // gridSize[0]), col*(frameSize[1] // gridSize[1]))
63 | bleh = row*4+col
64 | imagePoints[0:2, bleh] = center[0:2]
65 | checkerBoardWithCircle = cv.circle(np.repeat(checkerBoard[:, :, np.newaxis], 3, axis=2), center, radius, circleColor, thickeness)
66 |
67 | checkerBoardWithCircle = cv.putText(checkerBoardWithCircle, text, (50, frameSize[1] - 50), cv.FONT_HERSHEY_PLAIN, 2, textColor)
68 | cv.imshow("image2", checkerBoardWithCircle)
69 | cv.waitKey(5)
70 |
71 | #TODO capture the probe position
72 | #capturedWorldPoints[0:2, row+col] = probePosition[0:2]
73 |
74 | cv.destroyAllWindows()
75 |
76 | capturedWorldPoints[0:3, :] = np.array(\
77 | [[0.29800502, 0.0061638 , 0.19329104],\
78 | [0.29790819, 0.00609048, 0.12417287],\
79 | [0.29282362, 0.00589446, 0.0649954 ],\
80 | [0.29282882, 0.00585762, -0.00738237],\
81 | #----------------------------------------
82 | [0.23772776, 0.00590986, 0.19536735],\
83 | [0.23420618, 0.0054013 , 0.12486595],\
84 | [0.2346742 , 0.0055742 , 0.06042208],\
85 | [0.22915092, 0.00530273, -0.00476075],\
86 | #-----------------------------------------
87 | [0.17455278, 0.00515153, 0.19494631],\
88 | [0.17370129, 0.00505166, 0.12570471],\
89 | [0.1713025 , 0.00490197, 0.0634917 ],\
90 | [0.17024334, 0.00482908, -0.0078378 ],\
91 | #----------------------------------------
92 | [0.10822128, 0.004411 , 0.1962533 ],\
93 | [0.10675856, 0.00411283, 0.12804918],\
94 | [0.10582848, 0.00404256, 0.06549262],\
95 | [0.10449569, 0.00389199, -0.00398948],\
96 | #----------------------------------------
97 | [0.04599235, 0.0034341 , 0.1978229 ],\
98 | [0.04655383, 0.00365754, 0.12727729],\
99 | [0.04237613, 0.00342598, 0.06747411],\
100 | [0.04168911, 0.00318181, -0.00282326]]).T
101 |
102 | def getProjectionMatrix(imagePoints, capturedWorldPoints, projectionMatrix):
103 | projectionMatrix = np.linalg.lstsq(capturedWorldPoints.T, imagePoints.T)[0].T
104 | #projectionMatrix2 = np.linalg.solve(capturedWorldPoints.T, imagePoints.T).T
105 |
106 | print(projectionMatrix)
107 |
108 | def projectPointOnProbeTip(projectionMatix):
109 | k = -1
110 |
111 | while (k == -1):
112 | #TODO
113 | #currentWorldPoint =
114 |
115 | currentPixelPoint = np.matmul(projectionMatix, currentWorldPoint)
116 |
117 | img = np.zeros(frameSize)
118 | img = cv.line(img, (currentPixelPoint[0], 0), (currentPixelPoint[0], frameSize[0]), 1)
119 | img = cv.line(img, (0, currentPixelPoint[1]), (frameSize[1], currentPixelPoint[1]), 1)
120 |
121 | cv.imshow(img)
122 | k = cv.waitKey(1)
123 |
124 | cv.destroyAllWindows()
125 |
126 |
127 |
128 |
129 | if __name__ == "__main__":
130 | makeCheckerboard(gridSize, frameSize)
131 | pointGuidance(gridSize, checkerBoard, imagePoints, capturedWorldPoints)
132 | getProjectionMatrix(imagePoints, capturedWorldPoints, projectionMatrix)
133 |
--------------------------------------------------------------------------------
/learning/calibration/calibrate.py:
--------------------------------------------------------------------------------
1 | import pickle
2 | from scipy.sparse.linalg import lsqr
3 | import numpy as np
4 | from scipy.spatial.transform import Rotation as R
5 | from utils import load_opti_data, progress, GREY_OPTI_POS, RED_OPTI_ROT, GREY_OPTI_ROT, RED_OPTI_POS
6 |
7 | CALIBRATION_KEY = "red_calib"
8 |
9 |
10 | def main():
11 | tip = calibrate(CALIBRATION_KEY)
12 | pickle.dump(tip, open(CALIBRATION_KEY+".pkl", 'wb'))
13 |
14 |
15 | def fix_frame_opti(opti_data, start_frame, use_palm, use_markers):
16 | start = opti_data.index[start_frame]
17 | end = opti_data.index[start_frame + 1]
18 | # assert (opti_data.head_frame.iloc[start_frame] == opti_data.hand_frame.iloc[start_frame])
19 | # assert (opti_data.head_frame.iloc[start_frame + 1] == opti_data.hand_frame.iloc[start_frame + 1])
20 | missing_frames = np.linspace(start + 1, end - 1, end - start - 1).astype('int')
21 | for missing_frame in missing_frames:
22 | opti_data.loc[missing_frame] = None
23 | if use_markers:
24 | opti_data.at[missing_frame, 'marker_finger'] = opti_data.at[start, 'marker_finger']
25 | opti_data.at[missing_frame, 'marker_wrist'] = opti_data.at[start, 'marker_wrist']
26 | if use_palm:
27 | opti_data.at[missing_frame, 'marker_palm'] = opti_data.at[start, 'marker_palm']
28 | return opti_data
29 |
30 |
31 | def handle_missing_frames_opti(opti_data, use_palm, use_markers):
32 | print("Interpolating missing opti frames")
33 |
34 | # first let's deal with frame drops (probably from dropped UDP packets)
35 | opti_data = opti_data.set_index('frame_wrist')
36 | duplicate_frames = np.where(np.diff(opti_data.index.values) == 0)[0]
37 | # print(len(opti_data))
38 | opti_data.drop(opti_data.index[duplicate_frames], inplace=True)
39 | # print(len(opti_data))
40 |
41 | assert (np.sum(np.diff(opti_data.index.values) == 0) == 0) # this won't work if we have duplicated frame numbers
42 | frames_to_fix, = np.where(np.diff(opti_data.index.values) != 1)
43 | # print(frames_to_fix)
44 | for frame in frames_to_fix:
45 | opti_data = fix_frame_opti(opti_data, frame, use_palm, use_markers)
46 | progress(1)
47 | opti_data = opti_data.sort_index()
48 | opti_data.interpolate(inplace=True)
49 | print(f"Interpolated {len(frames_to_fix)} missing frames")
50 | return opti_data
51 |
52 |
53 | def find_tip_pose(pos, rot):
54 | pos = pos[::30]
55 | rot = rot[::30]
56 | size_of_eq = int(len(pos) * (len(pos) - 1) / 2)
57 | rotation = []
58 | position = []
59 | for index in range(len(pos)):
60 | for index2 in range(index+1, len(pos)):
61 | # rotation += R.from_dcm(rot[index].as_dcm() - rot[index2].as_dcm())
62 | rotation.append(rot[index].as_dcm() - rot[index2].as_dcm())
63 | position += [pos[index2] - pos[index]]
64 | position = np.reshape(position, (size_of_eq * 3, 1))
65 | rotation = np.reshape(rotation, (size_of_eq * 3, 3))
66 |
67 | # tip, res, rank, s = np.linalg.lstsq(rotation, position)
68 | tip, istop, itn, r1norm = lsqr(rotation, position)[:4]
69 | return tip
70 |
71 |
72 | def calibrate(key):
73 | print("processing opti file")
74 | opti_data, use_palm, use_markers = load_opti_data(key)
75 | print("done loading opti file")
76 |
77 | opti_data = handle_missing_frames_opti(opti_data, use_palm, use_markers)
78 | grey_pos = opti_data[GREY_OPTI_POS].values
79 | red_pos = opti_data[RED_OPTI_POS].values
80 | grey_rot = R.from_quat(opti_data[GREY_OPTI_ROT].values)
81 | red_rot = R.from_quat(opti_data[RED_OPTI_ROT].values)
82 |
83 | if CALIBRATION_KEY == "red_calib":
84 | tip = find_tip_pose(red_pos, red_rot)
85 | else:
86 | tip = find_tip_pose(grey_pos, grey_rot)
87 |
88 | print(tip)
89 | return tip
90 |
91 |
92 | if __name__ == "__main__":
93 | main()
94 |
--------------------------------------------------------------------------------
/learning/calibration/grey_calib.pkl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/learning/calibration/grey_calib.pkl
--------------------------------------------------------------------------------
/learning/calibration/red_calib.pkl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/learning/calibration/red_calib.pkl
--------------------------------------------------------------------------------
/learning/preprocessing/step1_extract_coordinates.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pickle
3 | # import quaternion
4 | import matplotlib.pyplot as plt
5 | from mpl_toolkits.mplot3d import Axes3D
6 | import numpy as np
7 | import pandas as pd
8 | from scipy.spatial.transform import Rotation as R
9 | # from preprocessing.optiTrackMarkers import get_opti_marker
10 | from settings import DATA_ROOT
11 | from utils import load_opti_data, \
12 | save_extracted_opti_data, progress, GREY_OPTI_POS, RED_OPTI_ROT, GREY_OPTI_ROT, \
13 | RED_OPTI_POS, BOARD_OPTI_ROT, BOARD_OPTI_POS
14 |
15 | TRIAL = "red_calib"
16 |
17 |
18 | def main():
19 | df_opti = extract_coordinates_opti(TRIAL)
20 | save_extracted_opti_data(df_opti, TRIAL)
21 |
22 |
23 | def fix_frame_opti(opti_data, start_frame, use_tray, use_markers):
24 | start = opti_data.index[start_frame]
25 | end = opti_data.index[start_frame + 1]
26 | # assert (opti_data.head_frame.iloc[start_frame] == opti_data.hand_frame.iloc[start_frame])
27 | # assert (opti_data.head_frame.iloc[start_frame + 1] == opti_data.hand_frame.iloc[start_frame + 1])
28 | missing_frames = np.linspace(start + 1, end - 1, end - start - 1).astype('int')
29 | for missing_frame in missing_frames:
30 | opti_data.loc[missing_frame] = None
31 | if use_markers:
32 | opti_data.at[missing_frame, 'marker_finger'] = opti_data.at[start, 'marker_finger']
33 | opti_data.at[missing_frame, 'marker_wrist'] = opti_data.at[start, 'marker_wrist']
34 | if use_tray:
35 | opti_data.at[missing_frame, 'marker_palm'] = opti_data.at[start, 'marker_palm']
36 | return opti_data
37 |
38 |
39 | def handle_missing_frames_opti(opti_data, use_tray, use_markers):
40 | print("Interpolating missing opti frames")
41 |
42 | # first let's deal with frame drops (probably from dropped UDP packets)
43 | opti_data = opti_data.set_index('frame_wrist')
44 | duplicate_frames = np.where(np.diff(opti_data.index.values) == 0)[0]
45 | # print(len(opti_data))
46 | opti_data.drop(opti_data.index[duplicate_frames], inplace=True)
47 | # print(len(opti_data))
48 |
49 | assert (np.sum(np.diff(opti_data.index.values) == 0) == 0) # this won't work if we have duplicated frame numbers
50 | frames_to_fix, = np.where(np.diff(opti_data.index.values) != 1)
51 | # print(frames_to_fix)
52 | for frame in frames_to_fix:
53 | opti_data = fix_frame_opti(opti_data, frame, use_tray, use_markers)
54 | progress(1)
55 | opti_data = opti_data.sort_index()
56 | opti_data.interpolate(inplace=True)
57 | print(f"Interpolated {len(frames_to_fix)} missing frames")
58 | return opti_data
59 |
60 |
61 | def extract_coordinates_opti(key):
62 | print("processing opti file")
63 | opti_data, use_board, use_markers = load_opti_data(key)
64 | print("done loading opti file")
65 |
66 | opti_data = handle_missing_frames_opti(opti_data, use_board, use_markers)
67 | markers = 0
68 | grey_pos = opti_data[GREY_OPTI_POS].values
69 | red_pos = opti_data[RED_OPTI_POS].values
70 | grey_rot = R.from_quat(opti_data[GREY_OPTI_ROT].values).inv()
71 | red_rot = R.from_quat(opti_data[RED_OPTI_ROT].values).inv()
72 | if use_board:
73 | board_pos = opti_data[BOARD_OPTI_POS].values
74 | board_rot = R.from_quat(opti_data[BOARD_OPTI_ROT].values).inv()
75 |
76 | red_tip = pickle.load(open(os.path.join(DATA_ROOT, 'calibration', 'red_calib.pkl'), 'rb'))
77 | grey_tip = pickle.load(open(os.path.join(DATA_ROOT, 'calibration', 'grey_calib.pkl'), 'rb'))
78 |
79 | grey_markers_adj = grey_pos + grey_rot.inv().apply(grey_tip)
80 | red_markers_adj = red_pos + red_rot.inv().apply(red_tip)
81 |
82 | # grey_markers_adj = grey_rot.apply(grey_pos - grey_tip)
83 | # red_markers_adj = red_rot.apply(red_pos - red_tip)
84 |
85 | two_tip_dist = grey_markers_adj - red_markers_adj
86 | plt.figure()
87 | plt.plot(np.linalg.norm(two_tip_dist, axis=1))
88 |
89 | plt.figure()
90 | plt.plot(grey_markers_adj)
91 | plt.figure()
92 | plt.plot(red_markers_adj)
93 | # plt.figure()
94 | # plt.plot(board_pos)
95 | # plt.figure()
96 | # plt.plot(board_pos)
97 | plt.show()
98 |
99 | return(package_data_opti(opti_data, grey_markers_adj, red_markers_adj, board_pos, board_rot.as_quat(), markers,
100 | use_board, use_markers))
101 |
102 |
103 | def package_data_opti(opti_data, grey_markers_adj, red_markers_adj, board_pos, board_rot, markers, use_board,
104 | use_markers):
105 | if use_board:
106 | data = np.concatenate((grey_markers_adj, red_markers_adj, board_pos, board_rot), axis=1)
107 | labels = ['g_x', 'g_y', 'g_z', 'r_x', 'r_y', 'r_z', 'board_x', 'board_y', 'board_z', 'board_qw', 'board_qx',
108 | 'board_qy', 'board_qz']
109 | else:
110 | data = np.concatenate((grey_markers_adj, red_markers_adj), axis=1)
111 | labels = ['g_x', 'g_y', 'g_z', 'g_qw', 'g_qx', 'g_qy', 'g_qz', 'r_x', 'r_y', 'r_z', 'r_qw', 'r_qx', 'r_qy',
112 | 'r_qz']
113 |
114 | if use_markers:
115 | data = np.concatenate((data, markers), axis=1)
116 | labels += [f"m{i}" for i in range(markers.shape[1])]
117 |
118 | concat = pd.DataFrame(data=data, columns=labels)
119 | return concat
120 |
121 |
122 | if __name__ == "__main__":
123 | main()
124 |
--------------------------------------------------------------------------------
/learning/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy
2 | pyquaternion
3 | seaborn
4 | numpy-quaternion
--------------------------------------------------------------------------------
/learning/settings.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | try:
4 | from local_settings import *
5 | except:
6 | DATA_ROOT = r'D:\board_viz_data'
7 |
8 | RECORDINGS_DIR = os.path.join(DATA_ROOT, "recordings")
9 | RECORDINGS_NATIVE_DIR = os.path.join(DATA_ROOT, "recordings_native")
10 | # RECORDINGS_NATIVE_DIR = os.path.join(DATA_ROOT, "recordings_interference")
11 | PROCESSED_RECORDINGS_DIR = os.path.join(DATA_ROOT, "processed")
12 | PREDICTIONS_DIR = os.path.join(DATA_ROOT, "predictions")
13 | CALIBRATION_DIR = os.path.join(DATA_ROOT, "rigid_body_calibration\hand\markers_18_12_05_19_23_00.txt")
14 | PREDICTION_MATLAB_DIR = os.path.join(DATA_ROOT, "MATLAB")
15 | TABFINDER_DIR = os.path.join(DATA_ROOT, "tap_finder")
16 |
17 |
18 |
--------------------------------------------------------------------------------
/pyboard/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/pyboard/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/pyboard/.idea/pyboard.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/pyboard/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/pyboard/capturedWorldPoints.pkl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/capturedWorldPoints.pkl
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle00.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle01.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle01.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle02.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle02.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle03.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle03.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle10.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle10.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle11.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle11.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle12.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle12.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle13.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle13.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle20.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle20.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle20.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle21.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle21.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle21.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle22.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle22.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle22.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle23.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle23.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle23.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle23.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle30.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle30.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle30.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle30.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle31.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle31.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle31.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle31.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle32.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle32.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle32.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle33.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle33.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle33.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle33.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle40.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle40.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle40.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle41.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle41.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle41.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle41.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle42.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle42.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle42.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle42.png
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle43.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle43.jpg
--------------------------------------------------------------------------------
/pyboard/checkerBoard_images/checkerBoardWithCircle43.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/checkerBoard_images/checkerBoardWithCircle43.png
--------------------------------------------------------------------------------
/pyboard/collect_data.py:
--------------------------------------------------------------------------------
1 | import pyrealtime as prt
2 | import struct
3 |
4 | from extract_opti_cordinate import extract_cord_layer
5 | from opti_lib import get_opti_source
6 | from projector_calib import projection_draw, get_pixel_point, get_board_position, get_red_pixel_point, \
7 | ProjectionCalibrate, get_board_rot
8 |
9 | CALIBRATE_PROJECTOR = False
10 | CALIBRATE_TIP_POS = False
11 | DEBUG = False
12 | FILTERED = True
13 | SEND_OVER_UDP = True
14 | USE_BOARD = True
15 | RECORD = False
16 | ALPHA = 0.3
17 | ALPHA_BOARD = 0.1
18 |
19 |
20 | def encode_udp(data):
21 | if USE_BOARD:
22 | # print(data['tip_pos'][2])
23 | return struct.pack("f" * 14,
24 | data['red_tip_pixel'][0], data['red_tip_pixel'][1], data['tip_pos'][2],
25 | data['red_top_pixel'][0], data['red_top_pixel'][1],
26 | data['board'][0], data['board'][1], data['board_pos'][2],
27 | data['grey_tip_pixel'][0], data['grey_tip_pixel'][1], data['tip_pos'][5],
28 | data['grey_top_pixel'][0], data['grey_top_pixel'][1], data['board_rot'][0])
29 |
30 | return struct.pack("f" * 8, data['red_tip_pixel'][0], data['red_tip_pixel'][1],
31 | data['tip_pos_opti'][0], data['tip_pos_opti'][1], data['tip_pos_opti'][2],
32 | data['opti']['red']['pos'][0], data['opti']['red']['pos'][1], data['opti']['red']['pos'][2])
33 |
34 |
35 | def main():
36 | opti = get_opti_source(show_plot=False, use_board=USE_BOARD, use_tray=False)
37 | if USE_BOARD:
38 | board_rot = get_board_rot(opti)
39 | # prt.PrintLayer(board_rot)
40 | # fps = prt.FigureManager(fps=10000)
41 | # prt.TimePlotLayer(board_rot, ylim=(-200, 200), n_channels=3, fig_manager=fps)
42 | if RECORD:
43 | prt.RecordLayer(opti, file_prefix="opti")
44 | if not CALIBRATE_TIP_POS:
45 | tip_pos = extract_cord_layer(opti, use_board=USE_BOARD)
46 | if CALIBRATE_PROJECTOR:
47 | ProjectionCalibrate(tip_pos, win_width=1920, win_height=1080)
48 | else:
49 | red_pixel_point = get_pixel_point(tip_pos, marker="RED_TIP")
50 | # prt.PrintLayer(red_pixel_point)
51 | if DEBUG:
52 | draw = projection_draw(red_pixel_point, win_width=1920, win_height=1080)
53 | else:
54 | red_top_pixel = get_pixel_point(opti, marker="RED_TOP")
55 | grey_pixel_point = get_pixel_point(tip_pos, marker="GREY_TIP")
56 | grey_top_pixel = get_pixel_point(opti, marker="GREY_TOP")
57 | if USE_BOARD:
58 | board_pixel_point = get_pixel_point(tip_pos, marker="BOARD")
59 | board_pos = get_board_position(tip_pos)
60 | if FILTERED:
61 | red_pixel_point = prt.ExponentialFilter(red_pixel_point, alpha=ALPHA)
62 | red_top_pixel = prt.ExponentialFilter(red_top_pixel, alpha=ALPHA)
63 | grey_pixel_point = prt.ExponentialFilter(grey_pixel_point, alpha=ALPHA)
64 | grey_top_pixel = prt.ExponentialFilter(grey_top_pixel, alpha=ALPHA)
65 | # prt.PrintLayer(grey_pixel_point)
66 | if SEND_OVER_UDP:
67 | data = prt.MergeLayer(None)
68 | data.set_input(red_pixel_point, "red_tip_pixel")
69 | data.set_input(red_top_pixel, "red_top_pixel")
70 | data.set_input(grey_pixel_point, "grey_tip_pixel")
71 | data.set_input(grey_top_pixel, "grey_top_pixel")
72 | data.set_input(tip_pos, "tip_pos")
73 | if USE_BOARD:
74 | if FILTERED:
75 | board_pixel_point = prt.ExponentialFilter(board_pixel_point, alpha=ALPHA_BOARD)
76 | # board_pos = prt.ExponentialFilter(board_pos, alpha=ALPHA_BOARD)
77 | #board_rot = prt.ExponentialFilter(board_rot, alpha=ALPHA_BOARD)
78 | data.set_input(board_pixel_point, "board")
79 | data.set_input(board_pos, "board_pos")
80 | data.set_input(board_rot, "board_rot")
81 |
82 | prt.UDPWriteLayer(data, port=8052, encoder=encode_udp)
83 | prt.LayerManager.session().run(show_monitor=False)
84 |
85 |
86 | if __name__ == "__main__":
87 | main()
--------------------------------------------------------------------------------
/pyboard/controller_lib.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from settings import PORT, BAUD, DATA_ROOT
4 | import serial
5 | import pyrealtime as prt
6 | import struct
7 | import numpy as np
8 |
9 | OSR = 256
10 | BYTES_PER_SAMPLE = 2
11 | RX_CHANNELS_ADC = 3
12 | BUFFER_SIZE = 8
13 | AGGREGATE = 1
14 | RX_CHANNELS = 9 # was 6
15 |
16 | NUM_SIGN_BYTES = 1
17 |
18 | NUM_BYTES = BYTES_PER_SAMPLE * RX_CHANNELS_ADC * AGGREGATE + NUM_SIGN_BYTES
19 | PACKET_FORMAT = '<' + 'B' * RX_CHANNELS_ADC * AGGREGATE * BYTES_PER_SAMPLE + ("B" * NUM_SIGN_BYTES)
20 |
21 | USE_NATIVE = True
22 | RECORD = False
23 |
24 | VICON_PORT = 9987
25 |
26 | FRAME_FORMAT = "IHhhhB"
27 | FRAME_SIZE = 13
28 | FRAMES_PER_PACKET = 40
29 |
30 |
31 | def convert_to_volts(data):
32 | center = 0x800000 >> int(((256 / OSR) - 1) * 3)
33 | if BYTES_PER_SAMPLE == 2:
34 | center = center >> 8
35 | return np.sign(data) * (np.abs(data) - center) / (center - 1) * -1.2
36 |
37 |
38 | def fix_signs(data, counts, buffer_size):
39 | signs = [(1 if x > buffer_size / 2 else -1) for x in counts]
40 | return data * signs
41 |
42 |
43 | def fix_signs_single_byte(data, sign_byte):
44 | values = [(sign_byte >> 0) & 0x03, (sign_byte >> 2) & 0x03, (sign_byte >> 4) & 0x03]
45 | # print(values)
46 | signs = [(1 if x >= 2 else -1) for x in values]
47 | return data * signs
48 |
49 |
50 | BYTES_PER_GROUP = 3
51 | AGGREGATE = 1
52 | DEMO = False
53 |
54 |
55 | def process(data):
56 | try:
57 | data = struct.unpack('>' + 'B' * 17, data)
58 | data = np.array([int(x) for x in data])
59 |
60 | except (ValueError, struct.error):
61 | print("Parse error")
62 | return None
63 |
64 | def convert_adc_bottom(data):
65 | all_data = np.zeros((3, 1))
66 | for i in range(3):
67 | adc_b = data[1 + i * 2] + ((data[0 + i * 2] & 0x0f) << 8)
68 | adc_b = adc_b - 2 ** 12 if adc_b > 2 ** 11 - 1 else adc_b
69 | all_data[i] = -adc_b
70 | result = all_data.T / 2048 * 1.2
71 | return result
72 |
73 | def convert_adc_top(data):
74 |
75 | all_data = np.zeros((6, 1))
76 | for i in range(3):
77 | adc_a = (data[0 + i * BYTES_PER_GROUP] << 4) + ((data[1 + i * BYTES_PER_GROUP] & 0xf0) >> 4)
78 | adc_b = data[2 + i * BYTES_PER_GROUP] + ((data[1 + i * BYTES_PER_GROUP] & 0x0f) << 8)
79 | adc_a = adc_a - 2 ** 12 if adc_a > 2 ** 11 - 1 else adc_a
80 | adc_b = adc_b - 2 ** 12 if adc_b > 2 ** 11 - 1 else adc_b
81 | all_data[i * 2 + 0] = -adc_a
82 | all_data[i * 2 + 1] = -adc_b
83 |
84 | result = all_data.T / 2048 * 1.2
85 | return result
86 |
87 | # top = convert_adc(data[1:10], 'top')
88 | # bottom = convert_adc(data[11:17], 'bottom')
89 | top = convert_adc_top(data[1:10])
90 | bottom = convert_adc_bottom(data[11:17])
91 | # bottom = bottom[0,[1,3,5]]
92 | all_data = np.hstack((np.atleast_2d(data[[0, 10]]), top, bottom))
93 | if np.any(all_data < -.02):
94 | print("bad packet")
95 | return None
96 | return all_data
97 |
98 |
99 | @prt.transformer
100 | def decimate(layer):
101 | return layer[0, :]
102 |
103 |
104 | @prt.transformer(multi_output=True)
105 | def split(data):
106 | return {"frame_num": data[:, 0:2], "adc": data[:, 2:]}
107 |
108 | last_data = [0,0]
109 | @prt.transformer
110 | def diff(data):
111 | global last_data
112 | d1 = data[0,:] - last_data
113 | d = np.diff(data, axis=0)
114 | last_data = data[-1,:]
115 | # print(data)
116 | return np.vstack((d1, d))
117 |
118 |
119 | @prt.transformer
120 | def get_channel(data):
121 | return data[:,0]
122 |
123 |
124 | @prt.transformer
125 | def get_energy(x):
126 | return np.atleast_2d(np.sum(x ** 2, axis=1)).T
127 |
128 |
129 | @prt.transformer
130 | def get_taps(energy_thresholds):
131 | energy = energy_thresholds['energy']
132 | if "thresholds" in energy_thresholds:
133 | thresholds = energy_thresholds['thresholds']
134 | else:
135 | thresholds = (0.000008, 0.000015)
136 | if np.max(energy) > thresholds[1]: # 0.000015:
137 | # print(2)
138 | return np.array([2])
139 | # else:
140 | # return False
141 | # return np.array([1])
142 | elif np.max(energy) > thresholds[0]: # 0.000008:
143 | # print(1)
144 | return np.array([1])
145 | else:
146 | return np.array([0])
147 |
148 |
149 | def decode_mag_file(line):
150 | data = np.array(eval(line.decode('utf-8')))
151 | return data
152 |
153 |
154 | def playback_device_data(key, show_plot=True, strip_frame_num=True):
155 | filename = os.path.join(DATA_ROOT, "recordings", f"mag_{key}.txt")
156 | if not os.path.exists(filename):
157 | filename = f"mag_{key}.txt"
158 | data = prt.PlaybackLayer(filename, rate=59, decoder=decode_mag_file, print_fps=True, loop=True)
159 | if strip_frame_num:
160 | split_data = split(data)
161 | adc = split_data.get_port("adc")
162 | else:
163 | adc = data
164 |
165 | if show_plot:
166 | fm = prt.FigureManager(fps=10000)
167 | prt.TimePlotLayer(split_data.get_port("adc"), window_size=5000, n_channels=RX_CHANNELS, ylim=(-0.1, 1), lw=1,
168 | fig_manager=fm)
169 | return data, adc
170 |
171 |
172 | def get_device_data(show_plot=True, buffer_size=BUFFER_SIZE):
173 | serial_port = serial.Serial(prt.find_serial_port(PORT), BAUD, timeout=5)
174 | serial_buffer = prt.FixedBuffer(buffer_size, use_np=True, shape=(RX_CHANNELS + 2,), axis=0)
175 | raw_data = prt.ByteSerialReadLayer.from_port(serial=serial_port, decoder=process, print_fps=True, preamble=b'UW',
176 | num_bytes=17, buffer=serial_buffer, multi_output=False)
177 | split_data = split(raw_data)
178 | # prt.PrintLayer(raw_data)
179 | if show_plot:
180 | fm = prt.FigureManager(fps=10000)
181 | if DEMO:
182 | filtered = prt.ExponentialFilter(split_data.get_port("adc"), alpha=.1, batch=True)
183 | prt.TimePlotLayer(filtered, window_size=5000, n_channels=RX_CHANNELS, ylim=(0, 0.2), lw=3, fig_manager=fm)
184 | else:
185 | # import scipy.signal
186 | # # filtered = prt.ExponentialFilter(split_data.get_port("adc"), alpha=1, batch=True)
187 | # prt.TimePlotLayer(split_data.get_port("adc"), window_size=5000, n_channels=RX_CHANNELS, ylim=(0, 1), lw=1, fig_manager=fm)
188 | # sos = scipy.signal.butter(5, [25,55], fs=472, btype="bandpass", output='sos')
189 | # filtered = prt.SOSFilter(split_data.get_port("adc"), sos, axis=0, shape=(9,))
190 | # energy = get_energy(filtered)
191 | #
192 | # prt.TimePlotLayer(energy, window_size=1000, n_channels=1, ylim=(0, .00005), lw=1)#, fig_manager=fm)
193 | # filtered2 = prt.ExponentialFilter(energy, alpha=.3, batch=True)
194 | # prt.TimePlotLayer(filtered2, window_size=1000, n_channels=1, ylim=(0, .00005), lw=1)#, fig_manager=fm)
195 | # # prt.Spectrogram(get_channel(filtered))
196 | # taps = get_taps(filtered2)
197 | # prt.TextPlotLayer(taps)
198 |
199 | prt.TimePlotLayer(split_data.get_port("adc"), window_size=5000, n_channels=RX_CHANNELS, ylim=(-0.1, 1), lw=1, fig_manager=fm)
200 | # prt.TimePlotLayer(diff(split_data.get_port("frame_num")), window_size=5000, n_channels=2, ylim=(0, 5), lw=1)
201 |
202 | return raw_data, split_data.get_port("adc")
203 |
204 |
205 | def encode_touch(x):
206 | return "Touch" if x else ""
207 |
208 |
209 | class ThresholdPlot(prt.TimePlotLayer):
210 | def __init__(self, port_in, thresholds, *args, **kwargs):
211 | super().__init__(port_in, *args, **kwargs)
212 | self.thresholds = thresholds
213 | self.threshold_series = []
214 |
215 | def transform(self, data):
216 | super().transform(data)
217 | return self.thresholds
218 |
219 | def post_init(self, data):
220 |
221 | for i, threshold in enumerate(self.thresholds):
222 | handle, = self.ax.plot([], [], '-', lw=self.lw, label=f"threshold_{i}")
223 | self.threshold_series.append(handle)
224 |
225 | self.fig_manager.fig.canvas.mpl_connect('button_press_event', self.on_click)
226 | super().post_init(data)
227 |
228 | def on_click(self, event):
229 | if event.xdata > self.window_size / 2:
230 | self.thresholds[1] = event.ydata
231 | else:
232 | self.thresholds[0] = event.ydata
233 |
234 | def update_fig(self, data):
235 | for (i, series) in enumerate(self.threshold_series):
236 | series.set_data([self.x_data[0], self.x_data[-1]], [self.thresholds[i], self.thresholds[i]])
237 |
238 | return super().update_fig(data) + self.threshold_series
239 |
240 |
241 | def detect_touch(data, show_plot=True):
242 |
243 | import scipy.signal
244 | # filtered = prt.ExponentialFilter(split_data.get_port("adc"), alpha=1, batch=True)
245 | sos = scipy.signal.butter(5, [25,80], fs=472, btype="bandpass", output='sos')
246 | filtered = prt.SOSFilter(data, sos, axis=0, shape=(9,))
247 | energy = get_energy(filtered)
248 |
249 | smoothed_energy = prt.ExponentialFilter(energy, alpha=.2, batch=True)
250 | # prt.TimePlotLayer(filtered2, window_size=1000, n_channels=1, ylim=(0, .000005), lw=1)#, fig_manager=fm)
251 | # prt.Spectrogram(get_channel(filtered), fs=472)
252 | energy_thresholds = prt.MergeLayer(None)
253 |
254 | energy_thresholds.set_input(smoothed_energy, "energy")
255 | if show_plot:
256 | thresholds = ThresholdPlot(smoothed_energy, thresholds=[.000002, .00001], window_size=1000, n_channels=1, ylim=(0, .00008), lw=1)
257 | thresholds.fig_manager.fps = 10
258 | energy_thresholds.set_input(thresholds, key="thresholds")
259 | taps = get_taps(energy_thresholds)
260 | # prt.TextPlotLayer(taps, encoder=encode_touch)
261 | return taps, filtered, smoothed_energy
262 |
--------------------------------------------------------------------------------
/pyboard/extract_opti_cordinate.py:
--------------------------------------------------------------------------------
1 | import struct
2 | import numpy as np
3 | import math
4 | from scipy.spatial.transform import Rotation as R
5 | import pyrealtime as prt
6 | import pickle
7 | import os
8 | from settings import DATA_ROOT
9 |
10 | USE_BOARD = False
11 | calib = 1
12 |
13 |
14 | @prt.transformer
15 | def extract_cord(data):
16 |
17 | global red_tip
18 | global calib
19 | global grey_tip
20 |
21 | if calib:
22 | red_tip = pickle.load(open(os.path.join(DATA_ROOT, 'calibration', 'red_calib.pkl'), 'rb'))
23 | grey_tip = pickle.load(open(os.path.join(DATA_ROOT, 'calibration', 'grey_calib.pkl'), 'rb'))
24 | calib = 0
25 |
26 | grey_pos = data['grey']['pos']
27 | red_pos = data['red']['pos']
28 | grey_rot = R.from_quat(data['grey']['rot'])
29 | red_rot = R.from_quat(data['red']['rot'])
30 |
31 | grey_markers_adj = grey_pos + grey_rot.apply(grey_tip)
32 | red_markers_adj = red_pos + red_rot.apply(red_tip)
33 |
34 | if USE_BOARD:
35 | board_pos = data['board']['pos']
36 | return [red_markers_adj[0], red_markers_adj[1], red_markers_adj[2], grey_markers_adj[0], grey_markers_adj[1], grey_markers_adj[2], board_pos[0], board_pos[1], board_pos[2]]
37 | return [red_markers_adj[0], red_markers_adj[1], red_markers_adj[2], grey_markers_adj[0], grey_markers_adj[1], grey_markers_adj[2]]
38 |
39 |
40 | def extract_cord_layer(data, use_board=False):
41 | global USE_BOARD
42 | USE_BOARD = use_board
43 | pos = extract_cord(data)
44 | return pos
45 |
--------------------------------------------------------------------------------
/pyboard/imagePoints.pkl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/imagePoints.pkl
--------------------------------------------------------------------------------
/pyboard/opti_lib.py:
--------------------------------------------------------------------------------
1 | from prt_natnet import NatNetLayer
2 | import pyrealtime as prt
3 | import numpy as np
4 |
5 | GREY_PROBE_RIGID_BODY_NAME = b"grey"
6 | RED_PROBE_RIGID_BODY_NAME = b"red"
7 | BOARD_RIGID_BODY_NAME = b"board"
8 | TRAY_RIGID_BODY_NAME = b"tray"
9 |
10 |
11 | @prt.transformer(multi_output=True)
12 | def parse(data):
13 | return {'pos': np.array(data[0]), 'rot': np.array(data[1])}
14 |
15 |
16 | def setup_fig(fig):
17 | ax1 = fig.add_subplot(421)
18 | ax2 = fig.add_subplot(422)
19 | ax3 = fig.add_subplot(423)
20 | ax4 = fig.add_subplot(424)
21 | ax5 = fig.add_subplot(425)
22 | ax6 = fig.add_subplot(426)
23 | ax7 = fig.add_subplot(427)
24 | ax8 = fig.add_subplot(428)
25 | return {f'{GREY_PROBE_RIGID_BODY_NAME}_pos': ax1, f'{GREY_PROBE_RIGID_BODY_NAME}_rot': ax2, f'{RED_PROBE_RIGID_BODY_NAME}_pos': ax3, f'{RED_PROBE_RIGID_BODY_NAME}_rot': ax4,
26 | f'{TRAY_RIGID_BODY_NAME}_pos': ax5, f'{TRAY_RIGID_BODY_NAME}_rot': ax6, f'{BOARD_RIGID_BODY_NAME}_pos': ax7, f'{BOARD_RIGID_BODY_NAME}_rot': ax8}
27 |
28 |
29 | def get_opti_source(show_plot=True, use_board=False, use_tray=False):
30 | bodies = [GREY_PROBE_RIGID_BODY_NAME, RED_PROBE_RIGID_BODY_NAME]
31 | if use_tray:
32 | bodies += [TRAY_RIGID_BODY_NAME]
33 | if use_board:
34 | bodies += [BOARD_RIGID_BODY_NAME]
35 |
36 | natnet = NatNetLayer(bodies_to_track=bodies, multi_output=True, print_fps=True, track_markers=True)
37 | # prt.PrintLayer(parse(natnet.get_port(RIGID_BODY_NAME)))
38 | frame_num = natnet.get_port("frame_num")
39 | parsed_grey = parse(natnet.get_port(GREY_PROBE_RIGID_BODY_NAME))
40 | parsed_red = parse(natnet.get_port(RED_PROBE_RIGID_BODY_NAME))
41 | if use_tray:
42 | parsed_tray = parse(natnet.get_port(TRAY_RIGID_BODY_NAME))
43 | if use_board:
44 | parsed_board = parse(natnet.get_port(BOARD_RIGID_BODY_NAME))
45 | markers = natnet.get_port('markers')
46 | # unlabled = natnet.get_port('unlabeled')
47 | if show_plot:
48 | fm = prt.FigureManager(setup_fig)
49 | prt.TimePlotLayer(parsed_grey.get_port('pos'), ylim=(-2, 2), n_channels=3, plot_key=f'{GREY_PROBE_RIGID_BODY_NAME}_pos',
50 | fig_manager=fm)
51 | prt.TimePlotLayer(parsed_grey.get_port('rot'), ylim=(-2,2), n_channels=4, plot_key=f'{GREY_PROBE_RIGID_BODY_NAME}_rot', fig_manager=fm)
52 | prt.TimePlotLayer(parsed_red.get_port('pos'), ylim=(-2, 2), n_channels=3, plot_key=f'{RED_PROBE_RIGID_BODY_NAME}_pos',
53 | fig_manager=fm)
54 | prt.TimePlotLayer(parsed_red.get_port('rot'), ylim=(-2,2), n_channels=4, plot_key=f'{RED_PROBE_RIGID_BODY_NAME}_rot', fig_manager=fm)
55 | if use_tray:
56 | prt.TimePlotLayer(parsed_tray.get_port('rot'), ylim=(-2,2), n_channels=4, plot_key=f'{TRAY_RIGID_BODY_NAME}_rot', fig_manager=fm)
57 | if use_board:
58 | prt.TimePlotLayer(parsed_board.get_port('rot'), ylim=(-2,2), n_channels=4, plot_key=f'{BOARD_RIGID_BODY_NAME}_rot', fig_manager=fm)
59 |
60 | data = prt.MergeLayer(None)
61 | data.set_input(frame_num, "frame_num")
62 | data.set_input(parsed_grey, "grey")
63 | data.set_input(parsed_red, "red")
64 | if use_tray:
65 | data.set_input(parsed_tray, "tray")
66 | if use_board:
67 | data.set_input(parsed_board, "board")
68 | data.set_input(markers, "markers")
69 | # data.set_input(unlabled, "unlabeled")
70 | # prt.PrintLayer(data)
71 | return data
72 |
--------------------------------------------------------------------------------
/pyboard/projectionMatrix.pkl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubicomplab/ar-debug-workbench/b8aee106f1626990e4f8bbd64547b76c5c1d3890/pyboard/projectionMatrix.pkl
--------------------------------------------------------------------------------
/pyboard/projector_calibration.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import cv2 as cv
3 | import math
4 | import pyrealtime as prt
5 | #size of projector frame
6 | frameSize = [1920, 1080]
7 | # size of checkerboard squares to project
8 | gridSize = [4, 3]
9 | numCaptures = (gridSize[0]+1) * (gridSize[1]+1)
10 |
11 | # world points in homogeneous coordinates
12 | """
13 | [X_1 X_2 X_3 ... X_n]
14 | [Y_1 Y_2 Y_3 ... Y_n]
15 | [Z_1 Z_2 Z_3 ... Z_n]
16 | [ 1 1 1 ... 1 ]
17 | """
18 | capturedWorldPoints = np.ones((4, numCaptures))
19 |
20 | #image points in homogeneous coordinates
21 | """
22 | [u_1 u_2 u_3 ... u_n]
23 | [v_1 v_2 v_3 ... v_n]
24 | [ 1 1 1 ... 1 ]
25 | """
26 | imagePoints = np.ones((3, numCaptures))
27 |
28 | #Projection matrix
29 | projectionMatrix = np.empty((3, 4))
30 |
31 |
32 |
33 | checkerBoard = np.zeros((frameSize[1], frameSize[0]))
34 |
35 |
36 | def makeCheckerboard():
37 | if frameSize[0] % gridSize[0] != 0:
38 | print("Frame width %i is not a multiple of checkerboard width %i. Use exact divsor for maximum precision." % (frameSize[0], gridSize[0]))
39 |
40 | if frameSize[1] % gridSize[1] != 0:
41 | print("Frame height %i is not a multiple of checkerboard height %i. Use exact divsor for maximum precision." % (frameSize[1], gridSize[1]))
42 |
43 | for row in range(0, np.shape(checkerBoard)[0]):
44 | for col in range(0, np.shape(checkerBoard)[1]):
45 | if ((math.floor(row / (frameSize[1] / gridSize[1]))) + (math.floor(col / (frameSize[0] / gridSize[0])))) % 2 == 0:
46 | checkerBoard[row, col] = 1
47 |
48 | cv.imshow("image", checkerBoard)
49 | cv.waitKey(0)
50 | cv.destroyAllWindows()
51 |
52 |
53 | @prt.transformer
54 | def pointGuidance(probePosition):
55 |
56 | global checkerBoard
57 | global imagePoints
58 | global capturedWorldPoints
59 | global projectionMatrix
60 |
61 | thickeness = 2
62 | radius = 20
63 | circleColor = (1,0,0)
64 |
65 | text = "Please place probe tip accurately on corner circled in blue. Press any key to capture position"
66 | textColor = (0.5,0.5,0.5)
67 |
68 | for row in range(0, gridSize[0]+1):
69 | for col in range(0, gridSize[1]+1):
70 | center = ( row*(frameSize[0] // gridSize[0]), col*(frameSize[1] // gridSize[1]))
71 | bleh = row*4+col
72 | imagePoints[0:2, bleh] = center[0:2]
73 | checkerBoardWithCircle = cv.circle(np.repeat(checkerBoard[:, :, np.newaxis], 3, axis=2), center, radius, circleColor, thickeness)
74 |
75 | checkerBoardWithCircle = cv.putText(checkerBoardWithCircle, text, (50, frameSize[1] - 50), cv.FONT_HERSHEY_PLAIN, 2, textColor)
76 | cv.imshow("image2", checkerBoardWithCircle)
77 | cv.waitKey(0)
78 |
79 | #TODO capture the probe position
80 | capturedWorldPoints[0:3, row*4+col] = probePosition
81 |
82 | cv.destroyAllWindows()
83 | projectionMatrix = np.linalg.lstsq(capturedWorldPoints.T, imagePoints.T)[0].T
84 |
85 | # capturedWorldPoints[0:3, :] = np.array(\
86 | # [[0.29800502, 0.0061638 , 0.19329104],\
87 | # [0.29790819, 0.00609048, 0.12417287],\
88 | # [0.29282362, 0.00589446, 0.0649954 ],\
89 | # [0.29282882, 0.00585762, -0.00738237],\
90 | # #----------------------------------------
91 | # [0.23772776, 0.00590986, 0.19536735],\
92 | # [0.23420618, 0.0054013 , 0.12486595],\
93 | # [0.2346742 , 0.0055742 , 0.06042208],\
94 | # [0.22915092, 0.00530273, -0.00476075],\
95 | # #-----------------------------------------
96 | # [0.17455278, 0.00515153, 0.19494631],\
97 | # [0.17370129, 0.00505166, 0.12570471],\
98 | # [0.1713025 , 0.00490197, 0.0634917 ],\
99 | # [0.17024334, 0.00482908, -0.0078378 ],\
100 | # #----------------------------------------
101 | # [0.10822128, 0.004411 , 0.1962533 ],\
102 | # [0.10675856, 0.00411283, 0.12804918],\
103 | # [0.10582848, 0.00404256, 0.06549262],\
104 | # [0.10449569, 0.00389199, -0.00398948],\
105 | # #----------------------------------------
106 | # [0.04599235, 0.0034341 , 0.1978229 ],\
107 | # [0.04655383, 0.00365754, 0.12727729],\
108 | # [0.04237613, 0.00342598, 0.06747411],\
109 | # [0.04168911, 0.00318181, -0.00282326]]).T
110 |
111 |
112 | @prt.transformer
113 | def projectPointOnProbeTip(probePosition):
114 | k = -1
115 | global projectPointOnProbeTip
116 | global projectionMatrix
117 | global currentWorldPoint
118 |
119 |
120 | while (k == -1):
121 | #TODO
122 | currentWorldPoint = np.array(probePosition)
123 |
124 | currentPixelPoint = np.matmul(projectionMatrix.T, currentWorldPoint.T)
125 |
126 | img = np.zeros(frameSize)
127 | img = cv.line(img, (currentPixelPoint[0], 0), (currentPixelPoint[0], frameSize[0]), 1)
128 | img = cv.line(img, (0, currentPixelPoint[1]), (frameSize[1], currentPixelPoint[1]), 1)
129 |
130 | cv.imshow(img)
131 | k = cv.waitKey(1)
132 |
133 | cv.destroyAllWindows()
134 |
135 |
136 | def projector(tip_pos):
137 | makeCheckerboard()
138 | pointGuidance(tip_pos)
139 |
140 | projectPointOnProbeTip(tip_pos)
141 |
142 | # getProjectionMatrix(imagePoints, capturedWorldPoints, projectionMatrix)
143 |
144 | if __name__ == "__main__":
145 | projector(0)
146 | # makeCheckerboard(gridSize, frameSize)
147 | # pointGuidance(gridSize, checkerBoard, imagePoints, capturedWorldPoints)
148 | # getProjectionMatrix(imagePoints, capturedWorldPoints, projectionMatrix)
149 |
--------------------------------------------------------------------------------
/pyboard/prt_natnet.py:
--------------------------------------------------------------------------------
1 | import pyrealtime as prt
2 |
3 | from NatNetClient import NatNetClient
4 |
5 |
6 | class NatNetLayer(prt.ProducerMixin, prt.ThreadLayer):
7 |
8 | def __init__(self, bodies_to_track, *args, track_markers=False, **kwargs):
9 | self.bodies_to_track = bodies_to_track
10 | self.id_to_name = {}
11 | self.track_markers = track_markers
12 | super().__init__(*args, **kwargs)
13 |
14 | # def on_data(self, id, frame_num, pos, rot):
15 | # if id in self.id_to_name:
16 | # self.supply_input({self.id_to_name[id]: (frame_num, pos, rot)})
17 | def on_data(self, frame_num, bodies, markers):
18 | ids_in_frame = list(bodies.keys())
19 | for id in ids_in_frame:
20 | bodies[self.id_to_name[id]] = bodies.pop(id)
21 | markers.pop(b'all')
22 | self.supply_input({'frame_num': frame_num, 'markers': markers, **bodies})
23 |
24 | def initialize(self):
25 | streamingClient = NatNetClient()
26 | streamingClient.run()
27 |
28 | bodies = streamingClient.get_rigid_bodies()
29 |
30 | for body_name in self.bodies_to_track:
31 | if body_name in bodies:
32 | # streamingClient.register_rigid_body_listener(bodies[body_name].id, self.on_data)
33 | self.id_to_name[bodies[body_name].id] = body_name
34 | print(body_name)
35 | else:
36 | print(body_name, " not found")
37 | print(bodies)
38 | streamingClient.register_callback(self.on_data)
--------------------------------------------------------------------------------
/pyboard/settings.py:
--------------------------------------------------------------------------------
1 | from local_settings import *
2 |
3 | # Port should be defined in local_settings
4 | # PORT = "COM25"
5 | DATA_ROOT = r'C:\board_viz_data'
6 | BAUD = 460800
--------------------------------------------------------------------------------
/pyboard/test.py:
--------------------------------------------------------------------------------
1 | import msvcrt
2 | import cv2 as cv
3 |
4 |
5 |
6 | image = cv.imread("checkerBoardWithCircle00.jpg")
7 | cv.imshow("image", image)
8 | k = cv.waitKey(0)
9 |
10 |
--------------------------------------------------------------------------------