-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtetra2pos.c
326 lines (269 loc) · 11.1 KB
/
tetra2pos.c
1
2
3
4
5
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#include "m_pd.h"
#include <string.h>
#include <math.h>
static t_class *tetra2pos_class;
typedef struct _tetra2pos {
t_object x_obj;
t_float edge_length; // in mm
t_float positions[4][3]; // actual positions in mm
int debug;
t_outlet *position_out; // position in mm
} t_tetra2pos;
static void calculate_positions(t_tetra2pos *x) {
t_float a = x->edge_length / 2; // half edge length
t_float sqrt3 = sqrt(3.0f);
t_float sqrt6 = sqrt(6.0f);
// Front mic
x->positions[0][0] = 0;
x->positions[0][1] = a * 2 / sqrt3;
x->positions[0][2] = -a / sqrt6;
// Left back mic
x->positions[1][0] = -a;
x->positions[1][1] = -a / sqrt3;
x->positions[1][2] = -a / sqrt6;
// Right back mic
x->positions[2][0] = a;
x->positions[2][1] = -a / sqrt3;
x->positions[2][2] = -a / sqrt6;
// Top mic
x->positions[3][0] = 0;
x->positions[3][1] = 0;
x->positions[3][2] = a * 3 / sqrt6;
}
static void tetra2pos_debug(t_tetra2pos *x, t_floatarg f) {
x->debug = (int)f;
}
static void tetra2pos_edge(t_tetra2pos *x, t_floatarg f) {
if (f <= 0) {
pd_error(x, "tetra2pos: edge length must be positive");
return;
}
x->edge_length = f;
calculate_positions(x);
if (x->debug) {
post("tetra2pos: edge length set to %.1f mm", x->edge_length);
post("tetra2pos: mic positions (mm):");
for (int i = 0; i < 4; i++) {
post(" %d: %.1f %.1f %.1f", i, x->positions[i][0], x->positions[i][1], x->positions[i][2]);
}
}
}
static void solve_linear_system(t_float A[3][3], t_float b[3], t_float result[3]) {
t_float det = A[0][0]*(A[1][1]*A[2][2] - A[1][2]*A[2][1])
- A[0][1]*(A[1][0]*A[2][2] - A[1][2]*A[2][0])
+ A[0][2]*(A[1][0]*A[2][1] - A[1][1]*A[2][0]);
if (fabs(det) < 0.0001f) {
result[0] = result[1] = result[2] = 0;
return;
}
for (int i = 0; i < 3; i++) {
t_float temp[3][3];
memcpy(temp, A, sizeof(temp));
for (int j = 0; j < 3; j++) {
temp[j][i] = b[j];
}
t_float det_i = temp[0][0]*(temp[1][1]*temp[2][2] - temp[1][2]*temp[2][1])
- temp[0][1]*(temp[1][0]*temp[2][2] - temp[1][2]*temp[2][0])
+ temp[0][2]*(temp[1][0]*temp[2][1] - temp[1][1]*temp[2][0]);
result[i] = det_i/det;
}
}
static void solve_position_toa(t_float distances[4], t_float positions[4][3], t_float result[3]) {
t_float A[3][3] = {{0}};
t_float b[3] = {0};
for (int i = 0; i < 3; i++) {
t_float dx = positions[i+1][0] - positions[0][0];
t_float dy = positions[i+1][1] - positions[0][1];
t_float dz = positions[i+1][2] - positions[0][2];
t_float d0_sq = distances[0] * distances[0];
t_float di_sq = distances[i+1] * distances[i+1];
t_float p0_sq = positions[0][0]*positions[0][0] +
positions[0][1]*positions[0][1] +
positions[0][2]*positions[0][2];
t_float pi_sq = positions[i+1][0]*positions[i+1][0] +
positions[i+1][1]*positions[i+1][1] +
positions[i+1][2]*positions[i+1][2];
A[i][0] = 2 * dx;
A[i][1] = 2 * dy;
A[i][2] = 2 * dz;
b[i] = d0_sq - di_sq - p0_sq + pi_sq;
}
solve_linear_system(A, b, result);
}
static void solve_position_tdoa(t_float distances[4], t_float positions[4][3], t_float result[3]) {
t_float tdoa[3];
for (int i = 0; i < 3; i++) {
tdoa[i] = distances[i + 1] - distances[0];
}
t_float min_error = 1e10;
t_float best_result[3] = {0, 0, 0};
t_float calc_distances[4] = {0};
// Find maximum absolute TDOA to help with range estimation
t_float max_abs_tdoa = 0;
for (int i = 0; i < 3; i++) {
if (fabs(tdoa[i]) > max_abs_tdoa) max_abs_tdoa = fabs(tdoa[i]);
}
// Start with a reasonable minimum distance that's at least max_abs_tdoa
t_float r1_start = fmax(1000, max_abs_tdoa);
t_float r1_end = 50000; // Maximum reasonable distance
t_float r1_step = r1_start * 0.1; // Initial step proportional to start distance
// Multiple passes with decreasing step sizes
for (int phase = 0; phase < 4; phase++) { // Back to 4 phases
// Adjust angular resolution based on phase
t_float angle_step = (phase < 2) ? M_PI / 8 :
(phase < 3) ? M_PI / 16 : M_PI / 32;
for (t_float r1 = r1_start; r1 < r1_end; r1 += r1_step) {
// Try multiple base angles for each radius
for (t_float angle = 0; angle < M_PI * 2; angle += angle_step) {
t_float test_distances[4];
test_distances[0] = r1;
test_distances[1] = r1 + tdoa[0];
test_distances[2] = r1 + tdoa[1];
test_distances[3] = r1 + tdoa[2];
// Skip if any distance is negative or too small
if (test_distances[1] < max_abs_tdoa ||
test_distances[2] < max_abs_tdoa ||
test_distances[3] < max_abs_tdoa) {
continue;
}
t_float test_result[3];
solve_position_toa(test_distances, positions, test_result);
t_float error = 0;
t_float max_calc_dist = 0;
for (int i = 0; i < 4; i++) {
t_float delta_x = test_result[0] - positions[i][0];
t_float delta_y = test_result[1] - positions[i][1];
t_float delta_z = test_result[2] - positions[i][2];
calc_distances[i] = sqrt(delta_x*delta_x + delta_y*delta_y + delta_z*delta_z);
if (calc_distances[i] > max_calc_dist) max_calc_dist = calc_distances[i];
}
// Calculate error with both absolute and relative components
for (int i = 0; i < 3; i++) {
t_float calc_tdoa = calc_distances[i+1] - calc_distances[0];
t_float abs_error = fabs(calc_tdoa - tdoa[i]);
error += abs_error + abs_error / (max_calc_dist + 1.0f);
}
if (error < min_error) {
min_error = error;
best_result[0] = test_result[0];
best_result[1] = test_result[1];
best_result[2] = test_result[2];
}
}
}
if (phase < 3) {
t_float best_r1 = calc_distances[0];
t_float window_factor = (phase < 2) ? 5.0f : 3.0f;
r1_start = best_r1 - r1_step * window_factor;
r1_end = best_r1 + r1_step * window_factor;
r1_step *= 0.1; // Each phase reduces step size by factor of 10
}
}
result[0] = best_result[0];
result[1] = best_result[1];
result[2] = best_result[2];
}
static void tetra2pos_relative(t_tetra2pos *x, t_symbol *s, int argc, t_atom *argv) {
(void)s;
if (argc != 4) {
pd_error(x, "tetra2pos: relative message expects 4 distances (mm)");
return;
}
t_float distances[4];
for (int i = 0; i < 4; i++) {
distances[i] = atom_getfloat(argv + i);
}
t_float position[3];
solve_position_tdoa(distances, x->positions, position);
// Force copy the values to ensure no optimization issues
t_float x_pos = position[0];
t_float y_pos = position[1];
t_float z_pos = position[2];
// Create and output the list
t_atom position_list[3];
SETFLOAT(&position_list[0], x_pos);
SETFLOAT(&position_list[1], y_pos);
SETFLOAT(&position_list[2], z_pos);
outlet_list(x->position_out, &s_list, 3, position_list);
if (x->debug) {
post("tetra2pos: relative distances (mm): %.1f %.1f %.1f %.1f",
distances[0], distances[1], distances[2], distances[3]);
post("tetra2pos: time differences (mm): %.1f %.1f %.1f",
distances[1] - distances[0],
distances[2] - distances[0],
distances[3] - distances[0]);
post("tetra2pos: position (mm): %.1f %.1f %.1f",
x_pos, y_pos, z_pos);
}
}
static void tetra2pos_list(t_tetra2pos *x, t_symbol *s, int argc, t_atom *argv) {
(void)s;
if (argc != 4) {
pd_error(x, "tetra2pos: expect 4 absolute distances (mm)");
return;
}
t_float distances[4];
for (int i = 0; i < 4; i++) {
distances[i] = atom_getfloat(argv + i);
}
t_float position[3];
solve_position_toa(distances, x->positions, position);
t_atom position_list[3];
SETFLOAT(&position_list[0], position[0]);
SETFLOAT(&position_list[1], position[1]);
SETFLOAT(&position_list[2], position[2]);
outlet_list(x->position_out, &s_list, 3, position_list);
if (x->debug) {
post("tetra2pos: absolute distances (mm): %.1f %.1f %.1f %.1f",
distances[0], distances[1], distances[2], distances[3]);
post("tetra2pos: position (mm): %.1f %.1f %.1f",
position[0], position[1], position[2]);
}
}
static void tetra2pos_positions(t_tetra2pos *x, t_symbol *s, int argc, t_atom *argv) {
(void)s; // Unused parameter
if (argc != 12) {
pd_error(x, "tetra2pos: positions message expects 12 values (x y z for each mic)");
return;
}
// Update positions array
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
x->positions[i][j] = atom_getfloat(argv + (i * 3 + j));
}
}
if (x->debug) {
post("tetra2pos: mic positions manually set (mm):");
for (int i = 0; i < 4; i++) {
post(" %d: %.1f %.1f %.1f", i, x->positions[i][0], x->positions[i][1], x->positions[i][2]);
}
}
}
static void tetra2pos_print(t_tetra2pos *x) {
post("tetra2pos: current mic positions (mm):");
for (int i = 0; i < 4; i++) {
post(" %d: %.1f %.1f %.1f", i, x->positions[i][0], x->positions[i][1], x->positions[i][2]);
}
}
static void *tetra2pos_new(t_floatarg edge) {
t_tetra2pos *x = (t_tetra2pos *)pd_new(tetra2pos_class);
x->position_out = outlet_new(&x->x_obj, &s_list);
x->edge_length = edge > 0 ? edge : 1000.0f;
x->debug = 0;
calculate_positions(x);
return (void *)x;
}
void tetra2pos_setup(void) {
tetra2pos_class = class_new(gensym("tetra2pos"),
(t_newmethod)tetra2pos_new,
0,
sizeof(t_tetra2pos),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addlist(tetra2pos_class, tetra2pos_list);
class_addmethod(tetra2pos_class, (t_method)tetra2pos_debug, gensym("debug"), A_FLOAT, 0);
class_addmethod(tetra2pos_class, (t_method)tetra2pos_edge, gensym("edge"), A_FLOAT, 0);
class_addmethod(tetra2pos_class, (t_method)tetra2pos_relative, gensym("relative"), A_GIMME, 0);
class_addmethod(tetra2pos_class, (t_method)tetra2pos_positions, gensym("positions"), A_GIMME, 0);
class_addmethod(tetra2pos_class, (t_method)tetra2pos_print, gensym("print"), 0);
}