ant-wireless 0.1.0.pre.20210810141303 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05bb146cd01ae909001401030d988ca1e14977d6159c5da9ee81736e66a17540
4
- data.tar.gz: b9240f153be4b7ca510344f41c94d8ad7bbb8a6dd2f72e41242c18641d772db8
3
+ metadata.gz: b16333eed832c3068cd6e95d167bffe8132aaba7543ccabd773d809fac840199
4
+ data.tar.gz: 92d089d1bcf5121dd46477befeabc33e43abbac10de105d3d94d176b54c9af8a
5
5
  SHA512:
6
- metadata.gz: 415ed825c123a9ca66debe50a52e406da5d83310127956fc8f4caba7b28eddfbb679528be081c5449267ae44a6dd9e820bbbf86ea48af8ba2ac66a2f77445bbf
7
- data.tar.gz: 64c3ade415dc32510a003e7cc41d1a61e532113541fd9de22ab5c4d31d8dfa797e099ff5bc05127ddf256438a1bf35a8461c70ebaf44c738af7fb5735dca0450
6
+ metadata.gz: 51dbc06b6c199f16404fb0d79fd78e10cf53c4ca68e221462d225e1dff171a51f5f1807da2ebe2719e45109e981ece26781090ebd583d4c8c01d6e1539c5e43a
7
+ data.tar.gz: da1492d614d52e83402c85bbeed0d45d03f7f101bd6b5036ae9bc7a488dfbd5df41ee5aa2a0a3b0a7b4e7019c4d95bc55d0167a3a6e77c1825565ca299ba492e
checksums.yaml.gz.sig ADDED
Binary file
data/History.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Release History for ruby-ant
2
2
 
3
3
  ---
4
+ ## v0.3.0 [2021-10-11] Michael Granger <ged@FaerieMUD.org>
4
5
 
6
+ Enhancements:
7
+
8
+ - Add frequency agility config to Channel
9
+ - Add tunable transmit power knob
10
+
11
+
12
+ ## v0.2.1 [2021-09-30] Michael Granger <ged@FaerieMUD.org>
13
+
14
+ Bugfixes:
15
+
16
+ - Add missing files to the manifest
17
+
18
+
19
+
20
+ ## v0.2.0 [2021-09-22] Michael Granger <ged@FaerieMUD.org>
21
+
22
+ Enhancements:
23
+
24
+ - Add channel period, search timeout, and introspection methods
25
+ - Add Ant.initialized? predicate
26
+ - Make the default response and event callback modules more useful.
27
+
28
+
29
+ ## v0.1.0 [2021-08-26] Michael Granger <ged@FaerieMUD.org>
30
+
31
+ First release.
5
32
 
data/README.md CHANGED
@@ -20,8 +20,13 @@ manner.
20
20
 
21
21
  ## Prerequisites
22
22
 
23
- * Ruby
24
- * Garmin USB ANT Stick (https://buy.garmin.com/en-US/US/p/10997/pn/010-01058-00)
23
+ * Ruby 2.7.x or later
24
+ * [Garmin USB ANT Stick][antstick]
25
+ * [ANT SDK][antsdk]
26
+
27
+ Note that we had trouble compiling the latest ANT SDK on some platforms, so we are currently using a modified version of it with a reworked build system for MacOS and FreeBSD. That is available under the same licensing terms at:
28
+
29
+ https://github.com/RavnGroup/Garmin-ANT-SDK
25
30
 
26
31
 
27
32
  ## Installation
@@ -52,6 +57,12 @@ This task will install dependencies, and do any other necessary setup for develo
52
57
 
53
58
  ## License
54
59
 
60
+ Portions of this code are from StaticCling, and are used under the
61
+ terms of the 3-Clause BSD License. Specifics can be found in the
62
+ appropriate files.
63
+
64
+ This software is:
65
+
55
66
  Copyright (c) 2021, Ravn Group
56
67
 
57
68
  Permission is hereby granted, free of charge, to any person obtaining
@@ -76,4 +87,5 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
76
87
 
77
88
  [ant]: https://www.thisisant.com/
78
89
  [antstick]: https://buy.garmin.com/en-US/US/p/10997/pn/010-01058-00
90
+ [antsdk]: https://www.thisisant.com/developer/resources/downloads/#software_tab
79
91
 
@@ -225,6 +225,17 @@ rant_s_init( int argc, VALUE *argv, VALUE _module )
225
225
  }
226
226
 
227
227
 
228
+ static VALUE
229
+ rant_s_initialized_p( VALUE _module )
230
+ {
231
+ #ifdef HAVE_ANT_ISINITIALIZED
232
+ const bool initialized = ANT_IsInitialized();
233
+ return initialized ? Qtrue : Qfalse;
234
+ #else
235
+ rb_notimplement();
236
+ #endif
237
+ }
238
+
228
239
 
229
240
  /*
230
241
  * call-seq:
@@ -291,13 +302,39 @@ rant_s_set_network_key( VALUE _module, VALUE network_number, VALUE key )
291
302
  }
292
303
 
293
304
  if ( !ANT_SetNetworkKey(ucNetNumber, (unsigned char *)pucKey) ) {
294
- rb_raise( rb_eRuntimeError, "could not set the network key." );
305
+ rant_log( "error", "could not set the network key." );
295
306
  }
296
307
 
297
308
  return Qtrue;
298
309
  }
299
310
 
300
311
 
312
+ /*
313
+ * call-seq:
314
+ * Ant.transmit_power = 4
315
+ *
316
+ * Set the transmit power level for all channels. Valid values are 0-4; default
317
+ * is 3 = 0dBm.
318
+ *
319
+ * # Set transmit power to -5 dBm
320
+ * Ant.transmit_power = 2
321
+ */
322
+ static VALUE
323
+ rant_s_transmit_power_eq( VALUE _module, VALUE power )
324
+ {
325
+ const unsigned char ucTransmitPower = NUM2CHR( power );
326
+ BOOL rval;
327
+
328
+ if ( ucTransmitPower < 0 || ucTransmitPower > 4 ) {
329
+ rb_raise( rb_eArgError, "expected a value between 0 and 4, got %d", ucTransmitPower );
330
+ }
331
+
332
+ rval = ANT_SetTransmitPower( ucTransmitPower );
333
+
334
+ return rval ? Qtrue : Qfalse;
335
+ }
336
+
337
+
301
338
  /*
302
339
  * call-seq:
303
340
  * Ant.assign_channel( channel, channel_type, network_number=0, extended_options=0x0, timeout=0 ) -> channel
@@ -463,6 +500,56 @@ rant_s_on_response( int argc, VALUE *argv, VALUE module )
463
500
  }
464
501
 
465
502
 
503
+ /*
504
+ * call-seq:
505
+ * Ant.request_capabilities
506
+ *
507
+ * Request the current ANT device's capabilities. These will be delivered
508
+ * via a callback to the #on_capabilities response callback, which by default
509
+ * extracts them into a Hash which is stored at Ant.capabilities.
510
+ *
511
+ */
512
+ static VALUE
513
+ rant_s_request_capabilities( VALUE module )
514
+ {
515
+ bool rval = ANT_RequestMessage( 0, MESG_CAPABILITIES_ID );
516
+ return rval ? Qtrue : Qfalse;
517
+ }
518
+
519
+
520
+ /*
521
+ * call-seq:
522
+ * Ant.request_serial_num
523
+ *
524
+ * Request the current ANT device's serial number. The result will be delivered
525
+ * via a callback to the #on_get_serial_num response callback, which by default
526
+ * extracts it and stores it at Ant.serial_number.
527
+ *
528
+ */
529
+ static VALUE
530
+ rant_s_request_serial_num( VALUE module )
531
+ {
532
+ bool rval = ANT_RequestMessage( 0, MESG_GET_SERIAL_NUM_ID );
533
+ return rval ? Qtrue : Qfalse;
534
+ }
535
+
536
+
537
+ /*
538
+ * call-seq:
539
+ * Ant.request_version
540
+ *
541
+ * Request the current device's ANT version. The result will be delivered
542
+ * via a callback to the #on_version response callback, which by default
543
+ * extracts it and stores it at Ant.hardware_version.
544
+ *
545
+ */
546
+ static VALUE
547
+ rant_s_request_version( VALUE module )
548
+ {
549
+ bool rval = ANT_RequestMessage( 0, MESG_VERSION_ID );
550
+ return rval ? Qtrue : Qfalse;
551
+ }
552
+
466
553
 
467
554
  /*
468
555
  * call-seq:
@@ -508,11 +595,13 @@ Init_ant_ext()
508
595
  rb_define_singleton_method( rant_mAnt, "device_serial_number", rant_s_device_serial_number, 0 );
509
596
 
510
597
  rb_define_singleton_method( rant_mAnt, "init", rant_s_init, -1 );
598
+ rb_define_singleton_method( rant_mAnt, "initialized?", rant_s_initialized_p, 0 );
511
599
  // rb_define_singleton_method( rant_mAnt, "init_ext", rant_s_init_ext, 4 );
512
600
  rb_define_singleton_method( rant_mAnt, "close", rant_s_close, 0 );
513
601
  rb_define_singleton_method( rant_mAnt, "reset", rant_s_reset, 0 );
514
602
 
515
603
  rb_define_singleton_method( rant_mAnt, "set_network_key", rant_s_set_network_key, 2 );
604
+ rb_define_singleton_method( rant_mAnt, "transmit_power=", rant_s_transmit_power_eq, 1 );
516
605
  rb_define_singleton_method( rant_mAnt, "assign_channel", rant_s_assign_channel, -1 );
517
606
 
518
607
  rb_define_singleton_method( rant_mAnt, "use_extended_messages=",
@@ -521,6 +610,9 @@ Init_ant_ext()
521
610
  rb_define_singleton_method( rant_mAnt, "on_response", rant_s_on_response, -1 );
522
611
  // EXPORT void ANT_UnassignAllResponseFunctions(); //Unassigns all response functions
523
612
 
613
+ rb_define_singleton_method( rant_mAnt, "request_capabilities", rant_s_request_capabilities, 0 );
614
+ rb_define_singleton_method( rant_mAnt, "request_serial_num", rant_s_request_serial_num, 0 );
615
+ rb_define_singleton_method( rant_mAnt, "request_version", rant_s_request_version, 0 );
524
616
 
525
617
  rb_define_singleton_method( rant_mAnt, "log_directory=", rant_s_log_directory_eq, 1 );
526
618
 
@@ -537,6 +629,7 @@ Init_ant_ext()
537
629
  EXPOSE_CONST( ANT_EXT_STRING_SIZE );
538
630
  EXPOSE_CONST( ANT_EXT_MESG_BITFIELD_DEVICE_ID );
539
631
  EXPOSE_CONST( ANT_EXT_MESG_BIFIELD_EXTENSION );
632
+ rb_define_const( rant_mAnt, "ANT_EXT_MESG_BITFIELD_EXTENSION", INT2FIX(ANT_EXT_MESG_BIFIELD_EXTENSION) );
540
633
  EXPOSE_CONST( ANT_EXT_MESG_BITFIELD_OVERWRITE_SHARED_ADR );
541
634
  EXPOSE_CONST( ANT_EXT_MESG_BITFIELD_TRANSMISSION_TYPE );
542
635
 
@@ -618,6 +711,9 @@ Init_ant_ext()
618
711
  EXPOSE_CONST( CAPABILITIES_SELECTIVE_DATA_UPDATE_ENABLED );
619
712
  EXPOSE_CONST( CAPABILITIES_ENCRYPTED_CHANNEL_ENABLED );
620
713
 
714
+ // Not in the header; this is taken from 9.5.7.4, ANT Message Protocol and Usage v5.1
715
+ rb_define_const( rant_mAnt, "CAPABILITIES_RFACTIVE_NOTIFICATION_ENABLED", INT2FIX(0) );
716
+
621
717
  EXPOSE_CONST( CHANNEL_NUMBER_MASK );
622
718
  EXPOSE_CONST( SEQUENCE_NUMBER_MASK );
623
719
  EXPOSE_CONST( SEQUENCE_NUMBER_ROLLOVER );
@@ -128,6 +128,12 @@ rant_channel_init( VALUE self, VALUE channel_number, VALUE channel_type, VALUE n
128
128
  rb_iv_set( self, "@network_number", network_number );
129
129
  rb_iv_set( self, "@extended_options", extended_options );
130
130
 
131
+ rb_iv_set( self, "@device_type", Qnil );
132
+ rb_iv_set( self, "@device_number", Qnil );
133
+ rb_iv_set( self, "@transmission_type", Qnil );
134
+ rb_iv_set( self, "@rf_frequency", Qnil );
135
+ rb_iv_set( self, "@agility_frequencies", Qnil );
136
+
131
137
  rb_hash_aset( registry, channel_number, self );
132
138
 
133
139
  return self;
@@ -173,8 +179,8 @@ rant_channel_set_channel_id( int argc, VALUE *argv, VALUE self )
173
179
  rb_scan_args( argc, argv, "31", &device_number, &device_type, &transmission_type, &timeout );
174
180
 
175
181
  usDeviceNumber = NUM2USHORT( device_number );
176
- ucDeviceType = NUM2USHORT( device_type );
177
- ucTransmissionType = NUM2USHORT( transmission_type );
182
+ ucDeviceType = NUM2CHR( device_type );
183
+ ucTransmissionType = NUM2CHR( transmission_type );
178
184
 
179
185
  if ( RTEST(timeout) )
180
186
  ulResponseTime = NUM2UINT( timeout );
@@ -186,13 +192,139 @@ rant_channel_set_channel_id( int argc, VALUE *argv, VALUE self )
186
192
  rb_raise( rb_eRuntimeError, "Failed to set the channel id." );
187
193
  }
188
194
 
195
+ rb_iv_set( self, "@device_type", device_type );
196
+ rb_iv_set( self, "@device_number", device_number );
197
+ rb_iv_set( self, "@transmission_type", transmission_type );
198
+
199
+ return Qtrue;
200
+ }
201
+
202
+
203
+ /*
204
+ * call-seq:
205
+ * channel.set_channel_period( period, timeout=0 )
206
+ *
207
+ * This message configures the messaging +period+ of a specific channel where:
208
+ * Messaging period = channel period time +period+ * 32768.
209
+ *
210
+ * E.g.: To send or receive a message at 4Hz, set the channel period to 8192 (32768/4).
211
+ *
212
+ * Note: The minimum acceptable channel period is difficult to specify as it is
213
+ * system dependent and depends on the number of configured channels and their use.
214
+ * Caution should be used to appropriately test the system when high data rates are
215
+ * used, especially in combination with multiple channels.
216
+ *
217
+ */
218
+ static VALUE
219
+ rant_channel_set_channel_period( int argc, VALUE *argv, VALUE self )
220
+ {
221
+ rant_channel_t *ptr = rant_get_channel( self );
222
+ VALUE period, timeout;
223
+ unsigned int ulResponseTime = 0;
224
+ unsigned short usMesgPeriod;
225
+ bool result;
226
+
227
+ rb_scan_args( argc, argv, "11", &period, &timeout );
228
+
229
+ usMesgPeriod = NUM2USHORT( period );
230
+ if ( RTEST(timeout) )
231
+ ulResponseTime = NUM2UINT( timeout );
232
+
233
+ result = ANT_SetChannelPeriod_RTO( ptr->channel_num, usMesgPeriod, ulResponseTime );
234
+
235
+ if ( !result )
236
+ rb_raise( rb_eRuntimeError, "Failed to set the channel period." );
237
+
238
+ return Qtrue;
239
+ }
240
+
241
+
242
+ /*
243
+ * call-seq:
244
+ * channel.set_channel_search_timeout( search_timeout, timeout=0 )
245
+ *
246
+ * Configure the length of time that the receiver will search for a channel
247
+ * before timing out. Note that a value of zero will disable high priority
248
+ * search mode, and a value of 255 sets an infinite search time-out. The
249
+ * exception to this is the AP1 module, which has only a high priority search
250
+ * mode. For AP1 only, a value of 0 is an immediate search timeout, and a value
251
+ * of 255 corresponds to approximately 10.5 minutes.
252
+ *
253
+ */
254
+ static VALUE
255
+ rant_channel_set_channel_search_timeout( int argc, VALUE *argv, VALUE self )
256
+ {
257
+ rant_channel_t *ptr = rant_get_channel( self );
258
+ VALUE search_timeout, timeout;
259
+ unsigned int ulResponseTime = 0;
260
+ unsigned char ucSearchTimeout;
261
+ bool result;
262
+
263
+ rb_scan_args( argc, argv, "11", &search_timeout, &timeout );
264
+
265
+ ucSearchTimeout = NUM2CHR( search_timeout );
266
+ if ( RTEST(timeout) )
267
+ ulResponseTime = NUM2UINT( timeout );
268
+
269
+ result = ANT_SetChannelSearchTimeout_RTO( ptr->channel_num, ucSearchTimeout, ulResponseTime );
270
+
271
+ if ( !result )
272
+ rb_raise( rb_eRuntimeError, "Failed to set the channel search timeout." );
273
+
274
+ return Qtrue;
275
+ }
276
+
277
+
278
+ /*
279
+ * call-seq:
280
+ * channel.set_channel_rf_freq( frequency )
281
+ *
282
+ * Set the ANT RF +frequency+.
283
+ *
284
+ */
285
+ static VALUE
286
+ rant_channel_set_channel_rf_freq( VALUE self, VALUE frequency )
287
+ {
288
+ rant_channel_t *ptr = rant_get_channel( self );
289
+ unsigned short ucRFFreq = NUM2USHORT( frequency );
290
+
291
+ if ( ucRFFreq > 124 ) {
292
+ rb_raise( rb_eArgError, "frequency must be between 0 and 124." );
293
+ }
294
+
295
+ ANT_SetChannelRFFreq( ptr->channel_num, ucRFFreq );
296
+
297
+ rb_iv_set( self, "@rf_frequency", frequency );
298
+
299
+ return Qtrue;
300
+ }
301
+
302
+
303
+ static VALUE
304
+ rant_channet_set_frequency_agility( VALUE self, VALUE freq1, VALUE freq2, VALUE freq3 )
305
+ {
306
+ rant_channel_t *ptr = rant_get_channel( self );
307
+ unsigned char ucFreq1 = NUM2CHR( freq1 ),
308
+ ucFreq2 = NUM2CHR( freq2 ),
309
+ ucFreq3 = NUM2CHR( freq3 );
310
+ VALUE frequencies = rb_ary_new_from_args( 3, freq1, freq2, freq3 );
311
+
312
+ if ( ucFreq1 > 124 || ucFreq2 > 124 || ucFreq3 > 124 ) {
313
+ rb_raise( rb_eArgError, "frequencies must be between 0 and 124." );
314
+ }
315
+
316
+ rant_log_obj( self, "info",
317
+ "Configuring channel %d to use frequency agility on %d, %d, and %d MHz.",
318
+ ptr->channel_num, ucFreq1 + 2400, ucFreq2 + 2400, ucFreq3 + 2400 );
319
+ ANT_ConfigFrequencyAgility( ptr->channel_num, ucFreq1, ucFreq2, ucFreq3 );
320
+
321
+ rb_ary_freeze( frequencies );
322
+ rb_iv_set( self, "@agility_frequencies", frequencies );
323
+
189
324
  return Qtrue;
190
325
  }
191
326
 
192
327
 
193
- // ANT_SetChannelPeriod_RTO(UCHAR ucANTChannel_, USHORT usMesgPeriod_, ULONG ulResponseTime_);
194
- // ANT_SetChannelSearchTimeout_RTO(UCHAR ucANTChannel_, UCHAR ucSearchTimeout_, ULONG ulResponseTime_);
195
- // ANT_SetChannelRFFreq_RTO(UCHAR ucANTChannel_, UCHAR ucRFFreq_, ULONG ulResponseTime_);
196
328
 
197
329
 
198
330
  /*
@@ -450,29 +582,6 @@ rant_channel_send_broadcast_data( VALUE self, VALUE data )
450
582
  }
451
583
 
452
584
 
453
- /*
454
- * call-seq:
455
- * channel.set_channel_rf_freq( frequency )
456
- *
457
- * Set the ANT RF +frequency+.
458
- *
459
- */
460
- static VALUE
461
- rant_channel_set_channel_rf_freq( VALUE self, VALUE frequency )
462
- {
463
- rant_channel_t *ptr = rant_get_channel( self );
464
- unsigned short ucRFFreq = NUM2USHORT( frequency );
465
-
466
- if ( ucRFFreq > 124 ) {
467
- rb_raise( rb_eArgError, "frequency must be between 0 and 124." );
468
- }
469
-
470
- ANT_SetChannelRFFreq( ptr->channel_num, ucRFFreq );
471
-
472
- return Qtrue;
473
- }
474
-
475
-
476
585
  void
477
586
  init_ant_channel()
478
587
  {
@@ -501,12 +610,18 @@ init_ant_channel()
501
610
  rb_attr( rant_cAntChannel, rb_intern("network_number"), 1, 0, 0 );
502
611
  rb_attr( rant_cAntChannel, rb_intern("extended_options"), 1, 0, 0 );
503
612
 
613
+ rb_attr( rant_cAntChannel, rb_intern("device_number"), 1, 0, 0 );
614
+ rb_attr( rant_cAntChannel, rb_intern("device_type"), 1, 0, 0 );
615
+ rb_attr( rant_cAntChannel, rb_intern("transmission_type"), 1, 0, 0 );
616
+ rb_attr( rant_cAntChannel, rb_intern("rf_frequency"), 1, 0, 0 );
617
+ rb_attr( rant_cAntChannel, rb_intern("agility_frequencies"), 1, 0, 0 );
618
+
504
619
  rb_define_method( rant_cAntChannel, "set_channel_id", rant_channel_set_channel_id, -1 );
505
- // rb_define_method( rant_cAntChannel, "set_channel_period",
506
- // rant_channel_set_channel_period, -1 );
507
- // rb_define_method( rant_cAntChannel, "set_channel_search_timeout",
508
- // rant_channel_set_channel_search_timeout, -1 );
620
+ rb_define_method( rant_cAntChannel, "set_channel_period", rant_channel_set_channel_period, -1 );
621
+ rb_define_method( rant_cAntChannel, "set_channel_search_timeout",
622
+ rant_channel_set_channel_search_timeout, -1 );
509
623
  rb_define_method( rant_cAntChannel, "set_channel_rf_freq", rant_channel_set_channel_rf_freq, 1 );
624
+ rb_define_method( rant_cAntChannel, "set_frequency_agility", rant_channet_set_frequency_agility, 3 );
510
625
 
511
626
  rb_define_method( rant_cAntChannel, "open", rant_channel_open, -1 );
512
627
  rb_define_method( rant_cAntChannel, "close", rant_channel_close, -1 );
@@ -14,6 +14,7 @@ have_header( 'ruby/thread.h' ) or
14
14
  abort "Your Ruby is too old!"
15
15
 
16
16
  have_func( 'ANT_Init', 'libant.h' )
17
+ have_func( 'ANT_IsInitialized', 'libant.h' )
17
18
  have_func( 'ANT_LibVersion', 'libant.h' )
18
19
  have_func( 'ANT_GetDeviceSerialNumber', 'libant.h' )
19
20
 
@@ -0,0 +1,197 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+ # vim: set nosta noet ts=4 sw=4:
4
+
5
+ require 'ant' unless defined?( Ant )
6
+
7
+ # Ant::BitVector -- a convenience class for manipulating and
8
+ # comparing bit vectors.
9
+ #
10
+ # This is a slightly-modified version of the same utility in StaticCling.
11
+ #
12
+ # == Synopsis
13
+ #
14
+ # require 'ant/bitvector'
15
+ #
16
+ # vector = Ant::BitVector.new
17
+ #
18
+ # vector.on( 4 ) # => 16
19
+ # vector.on( 12 ) # => 4112
20
+ # vector.toggle( 4 ) # => 4096
21
+ # vector.on?( 4 ) # => false
22
+ # vector.size # => 13
23
+ # vector.to_hex # => 0x1000
24
+ #
25
+ # vector2 = Ant::BitVector.new( 5 )
26
+ # vector > vector2 # => true
27
+ #
28
+ # vector2.each_with_index do |bit, i|
29
+ # puts "Bit %d is %s" % [ i + 1, bit.zero? ? 'off' : 'on' ]
30
+ # end
31
+ #
32
+ # Bit 1 is on
33
+ # Bit 2 is off
34
+ # Bit 3 is on
35
+ #
36
+ # == Version
37
+ #
38
+ # $Id$
39
+ #
40
+ # == Author
41
+ #
42
+ # * Mahlon E. Smith <mahlon@martini.nu>
43
+ #
44
+ # == License
45
+ #
46
+ # Copyright (c) 2000-2013, Mahlon E. Smith <mahlon@martini.nu>
47
+ #
48
+ # All rights reserved.
49
+ #
50
+ # Redistribution and use in source and binary forms, with or without
51
+ # modification, are permitted provided that the following conditions are met:
52
+ #
53
+ # * Redistributions of source code must retain the above copyright notice,
54
+ # this list of conditions and the following disclaimer.
55
+ #
56
+ # * Redistributions in binary form must reproduce the above copyright
57
+ # notice, this list of conditions and the following disclaimer in the
58
+ # documentation and/or other materials provided with the distribution.
59
+ #
60
+ # * Neither the name of the author, nor the names of contributors may be
61
+ # used to endorse or promote products derived from this software without
62
+ # specific prior written permission.
63
+ #
64
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
65
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
66
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
67
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
68
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
69
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
70
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
71
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
72
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
73
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
74
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
75
+ #
76
+ class Ant::BitVector
77
+ include Enumerable,
78
+ Comparable
79
+
80
+ ### Create a new bit vector object, optionally from a pre-existing
81
+ ### +init+ number (Any number that ruby supports natively should be
82
+ ### fine -- 0b, 0x, or decimal.)
83
+ def initialize( init=0 )
84
+ unless init.respond_to?( :to_i )
85
+ raise ArgumentError, "I don't know what to do with a %s object." % [ init.class.name ]
86
+ end
87
+
88
+ @bv = init.to_i
89
+ end
90
+
91
+
92
+ ######
93
+ public
94
+ ######
95
+
96
+ # let any additional methods fall through to Fixnum/Bignum objs,
97
+ # and return new vector objects. This allows for doing bitwise math
98
+ # or simple addition/subtraction on two BitVector objects.
99
+ %w{ % & * ** + - / << >> ^ | ~ }.each do |op|
100
+ define_method( op.to_sym ) do |arg|
101
+ res = @bv.send( op.to_sym, arg.bv )
102
+ return self.class.new( res )
103
+ end
104
+ end
105
+
106
+
107
+ ##
108
+ # Allow external access to the underlying Fixnum/Bignum
109
+ attr_reader :bv
110
+
111
+
112
+ ### Return the bit vector as a decimal.
113
+ def to_i
114
+ return @bv
115
+ end
116
+ alias_method :to_int, :to_i
117
+ alias_method :to_dec, :to_i
118
+
119
+
120
+ ### Return the bit vector as a binary string.
121
+ def to_bin
122
+ return "0b%s" % @bv.to_s(2)
123
+ end
124
+
125
+
126
+ ### Return the bit vector as a hexidecimal string.
127
+ def to_hex
128
+ return "0x%04x" % @bv
129
+ end
130
+
131
+
132
+ ### Return the length of the vector in bytes.
133
+ def size
134
+ return @bv.to_s(2).length
135
+ end
136
+
137
+
138
+ ### Switch a +bit+ on.
139
+ def on( bit )
140
+ @bv = @bv | ( 1 << bit )
141
+ end
142
+
143
+
144
+ ### Return boolean true if given +bit+ is currently on.
145
+ def on?( bit )
146
+ return @bv[ bit ].zero? ? false : true
147
+ end
148
+ alias_method :[], :on?
149
+
150
+
151
+ ### Switch a +bit+ off.
152
+ def off( bit )
153
+ @bv = @bv & ~( 1 << bit )
154
+ end
155
+
156
+
157
+ ### Return boolean true if given +bit+ is currently +off+.
158
+ def off?( bit )
159
+ return ! self.on?( bit )
160
+ end
161
+
162
+
163
+ ### Swap the current state of the given +bit+.
164
+ def toggle( bit )
165
+ @bv = @bv ^ ( 1 << bit )
166
+ end
167
+ alias_method :flip, :toggle
168
+
169
+
170
+ ### Set a +bit+ to +bool+ -- either true (on) or false (off).
171
+ ### Any value other than nil or false is treated as true.
172
+ ### This form also accepts ranges of bits, a la: vector[ 1..4 ] = true
173
+ def []=( bit, bool )
174
+ if bit.respond_to?( :each )
175
+ bit.each { |b| bool ? self.on( b ) : self.off( b ) }
176
+ else
177
+ bool ? self.on( bit ) : self.off( bit )
178
+ end
179
+ end
180
+
181
+
182
+ ### Yield each binary position, least significant +bit+ first.
183
+ def each
184
+ @bv.to_s(2).reverse.each_byte do |bit|
185
+ yield bit.chr.to_i
186
+ end
187
+ end
188
+
189
+
190
+ ### Comparision operator for Comparable mixin, fallthrough to
191
+ ### Fixnum/Bignum. Compares current state against +cmp+.
192
+ def <=>( cmp )
193
+ @bv <=> cmp.bv
194
+ end
195
+
196
+ end # class Ant::BitVector
197
+