phidgets_native 0.2.1 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +17 -2
- data/README.rdoc +9 -1
- data/Rakefile +7 -6
- data/examples/spatial.rb +3 -2
- data/examples/test_throttle.rb +40 -0
- data/ext/phidgets_native/extconf.rb +10 -3
- data/ext/phidgets_native/phidgets_native.c +124 -0
- data/ext/phidgets_native/phidgets_native.h +39 -0
- data/ext/phidgets_native/spatial.c +405 -9
- data/ext/phidgets_native/spatial_ruby.c +407 -1
- data/phidgets_native.gemspec +2 -1
- metadata +40 -24
@@ -3,6 +3,9 @@
|
|
3
3
|
const char MSG_COMPASS_CORRECTION_NOT_ARRAY[] = "compass correction must be a 13 element array";
|
4
4
|
const char MSG_COMPASS_CORRECTION_MUST_BE_FLOAT[] = "compass correction elements must be float or fixnum";
|
5
5
|
const char MSG_DATA_RATE_MUST_BE_NUM[] = "data rate must be fixnum";
|
6
|
+
const char MSG_AROUND_MUST_BE_NUMERIC[] = "rotation angles must be fixnums or floats";
|
7
|
+
const char MSG_IN_ORDER_MUST_BE_STRING[] = "rotation order must be a string";
|
8
|
+
const char MSG_INVALID_ROTATION_AXIS[] = "invalid rotation axis in rotation order, must be x, y, or z";
|
6
9
|
|
7
10
|
/*
|
8
11
|
* Document-class: PhidgetsNative::Spatial < PhidgetsNative::Device
|
@@ -13,6 +16,10 @@ const char MSG_DATA_RATE_MUST_BE_NUM[] = "data rate must be fixnum";
|
|
13
16
|
*/
|
14
17
|
|
15
18
|
void Init_phidgets_native_spatial(VALUE m_Phidget) {
|
19
|
+
// We need this for the orientation code mostly:
|
20
|
+
rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("matrix"));
|
21
|
+
|
22
|
+
// And now attach methods to the object:
|
16
23
|
VALUE c_Device = rb_const_get(m_Phidget, rb_intern("Device"));
|
17
24
|
|
18
25
|
VALUE c_Spatial = rb_define_class_under(m_Phidget,"Spatial",c_Device);
|
@@ -311,7 +318,134 @@ void Init_phidgets_native_spatial(VALUE m_Phidget) {
|
|
311
318
|
*/
|
312
319
|
rb_define_method(c_Spatial, "data_rate", spatial_data_rate_get, 0);
|
313
320
|
|
321
|
+
/*
|
322
|
+
* call-seq:
|
323
|
+
* direction_cosine_matrix( FixNum around_x, FixNum around_y, FixNum around_z, String in_order = 'xyz' ) -> Matrix
|
324
|
+
* This private method is used internally to return a single direction cosine
|
325
|
+
* Matrix that represents the combined translations provided around the x,y,
|
326
|
+
* and z axis. The optional in_order will translate around the axis in the
|
327
|
+
* order provided. Angles are expected to be in radians.
|
328
|
+
*/
|
329
|
+
rb_define_private_method(c_Spatial, "direction_cosine_matrix", spatial_direction_cosine_matrix, -1);
|
330
|
+
|
331
|
+
/*
|
332
|
+
* call-seq:
|
333
|
+
* accelerometer_to_roll_and_pitch -> Array
|
334
|
+
* This private method is used internally to return an array whose first double
|
335
|
+
* contains the roll and whose second contains the pitch component of the
|
336
|
+
* acceleration. Values are expressed in radians.
|
337
|
+
*/
|
338
|
+
rb_define_private_method(c_Spatial, "accelerometer_to_roll_and_pitch", spatial_accelerometer_to_roll_and_pitch, 0);
|
339
|
+
|
340
|
+
/*
|
341
|
+
* call-seq:
|
342
|
+
* gravity_to_roll_and_pitch -> Array
|
343
|
+
* This private method is used to return an array whose first double
|
344
|
+
* contains the roll and whose second contains the pitch component of gravity.
|
345
|
+
* Currently, this method merely calls and returns the accelerometer_to_roll_and_pitch
|
346
|
+
* method. Feel free to override this method with your own, for more accurate
|
347
|
+
* compass results. Values are expressed in radians.
|
348
|
+
*/
|
349
|
+
rb_define_private_method(c_Spatial, "gravity_to_roll_and_pitch", spatial_gravity_to_roll_and_pitch, 0);
|
350
|
+
|
351
|
+
/*
|
352
|
+
* Document-method: accelerometer_to_euler
|
353
|
+
* call-seq:
|
354
|
+
* accelerometer_to_euler -> Array
|
355
|
+
*
|
356
|
+
* This method returns a three element array of euler angles (yaw, roll, pitch)
|
357
|
+
* representing the vector of acceleration, as currently reflected by the
|
358
|
+
* spatial sensor.
|
359
|
+
*/
|
360
|
+
rb_define_method(c_Spatial, "accelerometer_to_euler", spatial_accelerometer_to_euler, 0);
|
361
|
+
|
362
|
+
/*
|
363
|
+
* Document-method: compass_bearing
|
364
|
+
* call-seq:
|
365
|
+
* compass_bearing -> Float
|
366
|
+
*
|
367
|
+
* This method returns a float representing the compass' bearing, perpendicular to
|
368
|
+
* the ground plane, in radians. A radian of 'pi' is where the usb cable plugs in,
|
369
|
+
* and a radian of 0 is the opposite end (front) of the device. Note that this
|
370
|
+
* method returns the 'magnetic' North, and not 'true' North. Also note that
|
371
|
+
* accuracy is highly determined by the private method 'gravity_to_roll_and_pitch'.
|
372
|
+
* Currently, gravity is merely assumed to be the output of the
|
373
|
+
* accelerometer_to_roll_and_pitch method. Feel free to extend the Spatial class
|
374
|
+
* and override the gravity_to_roll_and_pitch method, if you feel you have
|
375
|
+
* a better way to determine gravity. Your override's return will be used
|
376
|
+
* automatically by this method, once defined.
|
377
|
+
*/
|
378
|
+
rb_define_method(c_Spatial, "compass_bearing", spatial_compass_bearing, 0);
|
379
|
+
|
380
|
+
/*
|
381
|
+
* Document-method: compass_bearing_to_euler
|
382
|
+
* call-seq:
|
383
|
+
* compass_bearing_to_euler -> Float
|
384
|
+
*
|
385
|
+
* Probably this isn't too useful, but it returns a three element array, of which
|
386
|
+
* the third element is the compass_bearing.
|
387
|
+
*/
|
388
|
+
rb_define_method(c_Spatial, "compass_bearing_to_euler", spatial_compass_bearing_to_euler, 0);
|
389
|
+
|
390
|
+
/*
|
391
|
+
* Document-method: accelerometer_to_dcm
|
392
|
+
* call-seq:
|
393
|
+
* accelerometer_to_dcm -> Matrix
|
394
|
+
*
|
395
|
+
* This method returns a 3x3 Matrix containing a direction cosine Matrix which
|
396
|
+
* represents the rotation of the current accelerometer value.
|
397
|
+
*/
|
398
|
+
rb_define_method(c_Spatial, "accelerometer_to_dcm", spatial_accelerometer_to_dcm, 0);
|
399
|
+
|
400
|
+
/*
|
401
|
+
* Document-method: compass_bearing_to_dcm
|
402
|
+
* call-seq:
|
403
|
+
* compass_bearing_to_dcm -> Matrix
|
404
|
+
*
|
405
|
+
* This method returns a 3x3 direction cosine Matrix which represents the
|
406
|
+
* rotation of the current compass_bearing vector in 3d space.
|
407
|
+
*/
|
408
|
+
rb_define_method(c_Spatial, "compass_bearing_to_dcm", spatial_compass_bearing_to_dcm, 0);
|
409
|
+
|
410
|
+
/*
|
411
|
+
* Document-method: gyro_to_dcm
|
412
|
+
* call-seq:
|
413
|
+
* gyro_to_dcm -> Matrix
|
414
|
+
*
|
415
|
+
* This method returns a 3x3 direction cosine Matrix which represents the
|
416
|
+
* rotation of the current gyroscope vector.
|
417
|
+
*/
|
418
|
+
rb_define_method(c_Spatial, "gyro_to_dcm", spatial_gyro_to_dcm, 0);
|
419
|
+
|
420
|
+
/*
|
421
|
+
* Document-method: orientation_to_quaternion
|
422
|
+
* call-seq:
|
423
|
+
* orientation_to_quaternion -> Array
|
424
|
+
*
|
425
|
+
* This method returns a 4 element array consisting of floats, which represent
|
426
|
+
* the estimated AHRS orientation of the phidget.
|
427
|
+
*/
|
428
|
+
rb_define_method(c_Spatial, "orientation_to_quaternion", spatial_orientation_to_quaternion, 0);
|
429
|
+
|
430
|
+
/*
|
431
|
+
* Document-method: orientation_to_dcm
|
432
|
+
* call-seq:
|
433
|
+
* orientation_to_dcm -> Array
|
434
|
+
*
|
435
|
+
* This method returns a 3x3 direction cosine Matrix, which represent
|
436
|
+
* the estimated AHRS orientation of the phidget in 3d space.
|
437
|
+
*/
|
438
|
+
rb_define_method(c_Spatial, "orientation_to_dcm", spatial_orientation_to_dcm, 0);
|
314
439
|
|
440
|
+
/*
|
441
|
+
* Document-method: orientation_to_euler
|
442
|
+
* call-seq:
|
443
|
+
* orientation_to_euler -> Array
|
444
|
+
*
|
445
|
+
* This method returns a 3 element array, which represents the estimated
|
446
|
+
* AHRS orientation of the phidget in 3d space, in euler angles.
|
447
|
+
*/
|
448
|
+
rb_define_method(c_Spatial, "orientation_to_euler", spatial_orientation_to_euler, 0);
|
315
449
|
}
|
316
450
|
|
317
451
|
VALUE spatial_initialize(VALUE self, VALUE serial) {
|
@@ -323,6 +457,12 @@ VALUE spatial_initialize(VALUE self, VALUE serial) {
|
|
323
457
|
spatial_info->data_rate = DEFAULT_SPATIAL_DATA_RATE;
|
324
458
|
spatial_info->sample_rate = sample_create();
|
325
459
|
|
460
|
+
// Ahrs Defaults:
|
461
|
+
spatial_info->orientation_q[0] = 1.0;
|
462
|
+
spatial_info->orientation_q[1] = 0.0;
|
463
|
+
spatial_info->orientation_q[2] = 0.0;
|
464
|
+
spatial_info->orientation_q[3] = 0.0;
|
465
|
+
|
326
466
|
// Setup a spatial handle
|
327
467
|
CPhidgetSpatialHandle spatial = 0;
|
328
468
|
ensure(CPhidgetSpatial_create(&spatial));
|
@@ -446,6 +586,7 @@ VALUE spatial_zero_gyro(VALUE self) {
|
|
446
586
|
|
447
587
|
ensure(CPhidgetSpatial_zeroGyro((CPhidgetSpatialHandle) info->handle));
|
448
588
|
memset(spatial_info->gyroscope, 0, sizeof(double) * spatial_info->gyro_axes);
|
589
|
+
spatial_info->is_ahrs_initialized = false;
|
449
590
|
|
450
591
|
return Qnil;
|
451
592
|
}
|
@@ -481,9 +622,11 @@ VALUE spatial_compass_correction_set(VALUE self, VALUE compass_correction) {
|
|
481
622
|
}
|
482
623
|
|
483
624
|
spatial_info->is_compass_correction_known = true;
|
484
|
-
if (info->is_attached)
|
625
|
+
if (info->is_attached) {
|
485
626
|
spatial_set_compass_correction_by_array(
|
486
627
|
(CPhidgetSpatialHandle)info->handle, spatial_info->compass_correction);
|
628
|
+
spatial_info->is_ahrs_initialized = false;
|
629
|
+
}
|
487
630
|
}
|
488
631
|
|
489
632
|
return compass_correction;
|
@@ -531,3 +674,266 @@ VALUE spatial_data_rate_get(VALUE self) {
|
|
531
674
|
|
532
675
|
return INT2FIX(spatial_info->data_rate);
|
533
676
|
}
|
677
|
+
|
678
|
+
VALUE spatial_direction_cosine_matrix(int argc, VALUE *argv, VALUE self) {
|
679
|
+
VALUE around_x;
|
680
|
+
VALUE around_y;
|
681
|
+
VALUE around_z;
|
682
|
+
VALUE in_order;
|
683
|
+
|
684
|
+
const char *in_order_str = NULL;
|
685
|
+
double dbl_around_x;
|
686
|
+
double dbl_around_y;
|
687
|
+
double dbl_around_z;
|
688
|
+
|
689
|
+
rb_scan_args(argc, argv, "31", &around_x, &around_y, &around_z, &in_order);
|
690
|
+
|
691
|
+
if (TYPE(in_order) == T_STRING)
|
692
|
+
in_order_str = StringValueCStr(in_order);
|
693
|
+
else if( TYPE(in_order) != T_NIL)
|
694
|
+
rb_raise(rb_eTypeError, MSG_IN_ORDER_MUST_BE_STRING);
|
695
|
+
|
696
|
+
// Validate the inputs:
|
697
|
+
if ( (TYPE(around_x) == T_FLOAT) || (TYPE(around_x) == T_FIXNUM ) )
|
698
|
+
dbl_around_x = NUM2DBL(around_x);
|
699
|
+
else
|
700
|
+
rb_raise(rb_eTypeError, MSG_AROUND_MUST_BE_NUMERIC);
|
701
|
+
|
702
|
+
if ( (TYPE(around_y) == T_FLOAT) || (TYPE(around_y) == T_FIXNUM) )
|
703
|
+
dbl_around_y = NUM2DBL(around_y);
|
704
|
+
else
|
705
|
+
rb_raise(rb_eTypeError, MSG_AROUND_MUST_BE_NUMERIC);
|
706
|
+
|
707
|
+
if ( (TYPE(around_z) == T_FLOAT) || (TYPE(around_z) == T_FIXNUM) )
|
708
|
+
dbl_around_z = NUM2DBL(around_z);
|
709
|
+
else
|
710
|
+
rb_raise(rb_eTypeError, MSG_AROUND_MUST_BE_NUMERIC);
|
711
|
+
|
712
|
+
// Perform the actual rotation:
|
713
|
+
double mRetDcm[3][3] = {{1.0,0.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}};
|
714
|
+
|
715
|
+
if (euler_to_3x3dcm((double *) &mRetDcm, dbl_around_x, dbl_around_y,
|
716
|
+
dbl_around_z, (in_order_str) ? in_order_str : NULL) != 0)
|
717
|
+
rb_raise(rb_eTypeError, MSG_INVALID_ROTATION_AXIS);
|
718
|
+
|
719
|
+
// Return our results:
|
720
|
+
return double3x3_to_matrix_rb((double (*)[3])&mRetDcm);
|
721
|
+
}
|
722
|
+
|
723
|
+
VALUE spatial_accelerometer_to_roll_and_pitch(VALUE self) {
|
724
|
+
SpatialInfo *spatial_info = device_type_info(self);
|
725
|
+
|
726
|
+
if (!spatial_info->is_acceleration_known) return Qnil;
|
727
|
+
|
728
|
+
double dOrientEuler[3];
|
729
|
+
spatial_orientation_from_accel_and_compass( (double *)&dOrientEuler,
|
730
|
+
spatial_info->acceleration[0], spatial_info->acceleration[1], spatial_info->acceleration[2],
|
731
|
+
spatial_info->compass[0], spatial_info->compass[1], spatial_info->compass[2]);
|
732
|
+
|
733
|
+
// Construct a splat-able return of roll and pitch:
|
734
|
+
VALUE aryRet = rb_ary_new2(2);
|
735
|
+
|
736
|
+
rb_ary_store(aryRet, 0, rb_float_new(dOrientEuler[1])); // Roll
|
737
|
+
rb_ary_store(aryRet, 1, rb_float_new(dOrientEuler[0])); // Pitch
|
738
|
+
|
739
|
+
return aryRet;
|
740
|
+
}
|
741
|
+
|
742
|
+
VALUE spatial_gravity_to_roll_and_pitch(VALUE self) {
|
743
|
+
VALUE aryRollPitch = rb_funcall(self, rb_intern("accelerometer_to_roll_and_pitch"), 0);
|
744
|
+
|
745
|
+
return aryRollPitch;
|
746
|
+
}
|
747
|
+
|
748
|
+
VALUE spatial_accelerometer_to_euler(VALUE self) {
|
749
|
+
SpatialInfo *spatial_info = device_type_info(self);
|
750
|
+
|
751
|
+
if (!spatial_info->is_acceleration_known)
|
752
|
+
return Qnil;
|
753
|
+
|
754
|
+
VALUE aryRollPitch = rb_funcall(self, rb_intern("accelerometer_to_roll_and_pitch"), 0);
|
755
|
+
|
756
|
+
VALUE roll = rb_ary_shift(aryRollPitch);
|
757
|
+
VALUE pitch = rb_ary_shift(aryRollPitch);
|
758
|
+
|
759
|
+
VALUE aryRet = rb_ary_new2(3);
|
760
|
+
|
761
|
+
rb_ary_store(aryRet, 0, rb_float_new(NUM2DBL(roll) * -1.00 + M_PI_2));
|
762
|
+
rb_ary_store(aryRet, 1, rb_float_new(0.0));
|
763
|
+
rb_ary_store(aryRet, 2, pitch);
|
764
|
+
|
765
|
+
return aryRet;
|
766
|
+
}
|
767
|
+
|
768
|
+
VALUE spatial_compass_bearing(VALUE self) {
|
769
|
+
// Get the Compass Values:
|
770
|
+
VALUE aryCompass = rb_funcall(self, rb_intern("compass"), 0);
|
771
|
+
|
772
|
+
if ( (TYPE(aryCompass) == T_NIL) || (RARRAY_LEN(aryCompass) != 3) )
|
773
|
+
return Qnil;
|
774
|
+
|
775
|
+
double dbl_comp_x = NUM2DBL(rb_ary_shift(aryCompass));
|
776
|
+
double dbl_comp_y = NUM2DBL(rb_ary_shift(aryCompass));
|
777
|
+
double dbl_comp_z = NUM2DBL(rb_ary_shift(aryCompass));
|
778
|
+
|
779
|
+
// Get the Acceleration Values (All we really need here is 'z'):
|
780
|
+
VALUE aryAccel = rb_funcall(self, rb_intern("accelerometer"), 0);
|
781
|
+
|
782
|
+
if ( (TYPE(aryAccel) == T_NIL) || (RARRAY_LEN(aryAccel) != 3) )
|
783
|
+
return Qnil;
|
784
|
+
|
785
|
+
double dbl_accel_x = NUM2DBL(rb_ary_shift(aryAccel));
|
786
|
+
double dbl_accel_y = NUM2DBL(rb_ary_shift(aryAccel));
|
787
|
+
double dbl_accel_z = NUM2DBL(rb_ary_shift(aryAccel));
|
788
|
+
|
789
|
+
// Get the Gravity Vector:
|
790
|
+
VALUE aryGrvRollPitch = rb_funcall(self, rb_intern("gravity_to_roll_and_pitch"), 0);
|
791
|
+
|
792
|
+
double dbl_grv_roll = NUM2DBL(rb_ary_shift(aryGrvRollPitch));
|
793
|
+
double dbl_grv_pitch = NUM2DBL(rb_ary_shift(aryGrvRollPitch));
|
794
|
+
|
795
|
+
/*
|
796
|
+
* Yaw Angle - about axis 2
|
797
|
+
* tan(yaw) = (mz * sin(roll) – my * cos(roll)) /
|
798
|
+
* (mx * cos(pitch) + my * sin(pitch) * sin(roll) + mz * sin(pitch) * cos(roll))
|
799
|
+
* Use Atan2 to get our range in (-180 - 180)
|
800
|
+
*
|
801
|
+
* Yaw angle == 0 degrees when axis 0 is pointing at magnetic north
|
802
|
+
*/
|
803
|
+
double dbl_yaw = atan2( (dbl_comp_z * sin(dbl_grv_roll)) - (dbl_comp_y * cos(dbl_grv_roll)),
|
804
|
+
(dbl_comp_x * cos(dbl_grv_pitch)) +
|
805
|
+
(dbl_comp_y * sin(dbl_grv_pitch) * sin(dbl_grv_roll)) +
|
806
|
+
(dbl_comp_z * sin(dbl_grv_pitch) * cos(dbl_grv_roll)) );
|
807
|
+
|
808
|
+
// And we're done:
|
809
|
+
return rb_float_new( (dbl_accel_z < 0) ? ( dbl_yaw - M_PI_2 ) : ( dbl_yaw - M_PI * 1.5 ) );
|
810
|
+
}
|
811
|
+
|
812
|
+
VALUE spatial_compass_bearing_to_euler(VALUE self) {
|
813
|
+
VALUE compass_bearing = rb_funcall(self, rb_intern("compass_bearing"), 0);
|
814
|
+
|
815
|
+
if (TYPE(compass_bearing) == T_NIL)
|
816
|
+
return Qnil;
|
817
|
+
|
818
|
+
VALUE aryRet = rb_ary_new2(3);
|
819
|
+
|
820
|
+
rb_ary_store(aryRet, 0, rb_float_new(0.0));
|
821
|
+
rb_ary_store(aryRet, 1, rb_float_new(0.0));
|
822
|
+
rb_ary_store(aryRet, 2, compass_bearing);
|
823
|
+
|
824
|
+
return aryRet;
|
825
|
+
}
|
826
|
+
|
827
|
+
VALUE spatial_accelerometer_to_dcm(VALUE self) {
|
828
|
+
VALUE aryAccelEu = rb_funcall(self, rb_intern("accelerometer_to_euler"), 0);
|
829
|
+
|
830
|
+
if ( (TYPE(aryAccelEu) == T_NIL) || (RARRAY_LEN(aryAccelEu) != 3) )
|
831
|
+
return Qnil;
|
832
|
+
|
833
|
+
VALUE angles[3];
|
834
|
+
for(unsigned int i=0; i<3; i++)
|
835
|
+
angles[i] = rb_ary_shift(aryAccelEu);
|
836
|
+
|
837
|
+
return rb_funcall(self, rb_intern("direction_cosine_matrix"), 3, angles[0], angles[1], angles[2]);
|
838
|
+
}
|
839
|
+
|
840
|
+
VALUE spatial_compass_bearing_to_dcm(VALUE self) {
|
841
|
+
VALUE aryCompassEu = rb_funcall(self, rb_intern("compass_bearing_to_euler"), 0);
|
842
|
+
|
843
|
+
if ( (TYPE(aryCompassEu) == T_NIL) || (RARRAY_LEN(aryCompassEu) != 3) )
|
844
|
+
return Qnil;
|
845
|
+
|
846
|
+
VALUE angles[3];
|
847
|
+
for(unsigned int i=0; i<3; i++)
|
848
|
+
angles[i] = rb_ary_shift(aryCompassEu);
|
849
|
+
|
850
|
+
return rb_funcall(self, rb_intern("direction_cosine_matrix"), 3, angles[0], angles[1], angles[2]);
|
851
|
+
}
|
852
|
+
|
853
|
+
VALUE spatial_gyro_to_dcm(VALUE self) {
|
854
|
+
SpatialInfo *spatial_info = device_type_info(self);
|
855
|
+
|
856
|
+
if ( (!spatial_info->is_gyroscope_known) || (spatial_info->gyro_axes != 3) )
|
857
|
+
return Qnil;
|
858
|
+
|
859
|
+
double mRetDcm[3][3];
|
860
|
+
memcpy(&mRetDcm, &spatial_info->gyroscope_dcm, sizeof(mRetDcm));
|
861
|
+
|
862
|
+
// Return our results:
|
863
|
+
return double3x3_to_matrix_rb((double (*)[3])&mRetDcm);
|
864
|
+
}
|
865
|
+
|
866
|
+
VALUE spatial_orientation_to_quaternion(VALUE self) {
|
867
|
+
SpatialInfo *spatial_info = device_type_info(self);
|
868
|
+
|
869
|
+
// NOTE: I'm reusing this conditional from the dcm test. I think we don't need
|
870
|
+
// to implement a separated is_orientation_known function as the two conditions
|
871
|
+
// are identical:
|
872
|
+
if ( (!spatial_info->is_gyroscope_known) || (spatial_info->gyro_axes != 3) )
|
873
|
+
return Qnil;
|
874
|
+
|
875
|
+
float fOrientationQ[4];
|
876
|
+
float fTranslatedQ[4];
|
877
|
+
double dblRetQ[4];
|
878
|
+
|
879
|
+
// Grabe the magwick quaternion:
|
880
|
+
memcpy(&fOrientationQ, &spatial_info->orientation_q, sizeof(fOrientationQ));
|
881
|
+
|
882
|
+
// Translate it into 'our' (aka opengl's) cordinate space:
|
883
|
+
spatial_madgeq_to_openglq((float *) &fOrientationQ, (float *) &fTranslatedQ);
|
884
|
+
|
885
|
+
// The Madgwick algorithem uses floats, and ruby wants doubles. Let's cast!
|
886
|
+
for(int i=0; i<4; i++)
|
887
|
+
dblRetQ[i] = (double) fTranslatedQ[i];
|
888
|
+
|
889
|
+
// Return our results:
|
890
|
+
return double_array_to_rb((double *) &dblRetQ, 4);
|
891
|
+
}
|
892
|
+
|
893
|
+
VALUE spatial_orientation_to_dcm(VALUE self) {
|
894
|
+
SpatialInfo *spatial_info = device_type_info(self);
|
895
|
+
|
896
|
+
if ( (!spatial_info->is_gyroscope_known) || (spatial_info->gyro_axes != 3) )
|
897
|
+
return Qnil;
|
898
|
+
|
899
|
+
float fOrientationQ[4];
|
900
|
+
float fTranslatedQ[4];
|
901
|
+
double dblRetDcm[3][3];
|
902
|
+
|
903
|
+
// Grabe the magwick quaternion:
|
904
|
+
memcpy(&fOrientationQ, &spatial_info->orientation_q, sizeof(fOrientationQ));
|
905
|
+
|
906
|
+
// Translate it into 'our' (aka opengl's) cordinate space:
|
907
|
+
spatial_madgeq_to_openglq((float *) &fOrientationQ, (float *) &fTranslatedQ);
|
908
|
+
|
909
|
+
quat_to_dcm((float *)&fTranslatedQ, (double (*)[3]) &dblRetDcm);
|
910
|
+
|
911
|
+
return double3x3_to_matrix_rb((double (*)[3])&dblRetDcm);
|
912
|
+
}
|
913
|
+
|
914
|
+
VALUE spatial_orientation_to_euler(VALUE self) {
|
915
|
+
SpatialInfo *spatial_info = device_type_info(self);
|
916
|
+
|
917
|
+
if ( (!spatial_info->is_gyroscope_known) || (spatial_info->gyro_axes != 3) )
|
918
|
+
return Qnil;
|
919
|
+
|
920
|
+
float fOrientationQ[4];
|
921
|
+
float fTranslatedQ[4];
|
922
|
+
float fEuler[3];
|
923
|
+
double dblRetEuler[3];
|
924
|
+
|
925
|
+
// Grabe the magwick quaternion:
|
926
|
+
memcpy(&fOrientationQ, &spatial_info->orientation_q, sizeof(fOrientationQ));
|
927
|
+
|
928
|
+
// Translate it into 'our' (aka opengl's) cordinate space:
|
929
|
+
spatial_madgeq_to_openglq((float *) &fOrientationQ, (float *) &fTranslatedQ);
|
930
|
+
|
931
|
+
quat_to_euler((float *)&fTranslatedQ, (float (*))&fEuler);
|
932
|
+
|
933
|
+
// The Madgwick algorithem uses floats, and ruby wants doubles. Let's cast!
|
934
|
+
for(int i=0; i<3; i++)
|
935
|
+
dblRetEuler[i] = (double) fEuler[i];
|
936
|
+
|
937
|
+
// Return our results:
|
938
|
+
return double_array_to_rb((double *) &dblRetEuler, 3);
|
939
|
+
}
|