This is an extension for using the ANT ultra-low power wireless protocol via the Garmin USB ANT Stick. ANT can be used to send information wirelessly from one device to another device, in a robust and flexible manner.
Version control revision
The default range of frequencies for the channel period
A Range for matching valid ANT device numbers
A Range for matching valid ANT device types (6 least signficant bits)
The valid offsets for the “RF Frequency” setting; this is an offset from 2400Hz.
Package version
Assign a channel and return an Ant::Channel
object for it. Channel
assignment reserves a channel number and assigns the type and network number to the channel. The optional extended assignment byte allows for the following features to be enabled:
EXT_PARAM_FREQUENCY_AGILITY
enable frequency agility
EXT_PARAM_BACKGROUND_SCANNING
enable background scanning
EXT_PARAM_FAST_CHANNEL_INIT
enable fast channel initiation
EXT_PARAM_ASYNC_TRANSMIT
enable asynchronous transmission
static VALUE
rant_s_assign_channel( int argc, VALUE *argv, VALUE _module )
{
unsigned char ucChannel,
ucChannelType,
ucNetworkNumber = 0,
ucExtend = 0,
ulResponseTime = 0;
VALUE channel,
channel_type,
network_number,
extended_options,
timeout;
VALUE args[4];
rb_scan_args( argc, argv, "23", &channel, &channel_type, &network_number, &extended_options, &timeout );
ucChannel = NUM2CHR( channel );
ucChannelType = NUM2CHR( channel_type );
if ( RTEST(network_number) ) {
ucNetworkNumber = NUM2CHR( network_number );
}
if ( RTEST(extended_options) ) {
ucExtend = NUM2CHR( extended_options );
}
if ( RTEST(timeout) ) {
ulResponseTime = NUM2CHR( timeout );
}
if ( !ANT_AssignChannelExt_RTO(ucChannel, ucChannelType, ucNetworkNumber, ucExtend, ulResponseTime) ) {
rb_raise( rb_eRuntimeError, "Couldn't assign channel %d", ucChannel );
}
rant_log( "info", "Assigned channel %d (0x%02x) to network %d {0x%02x}.",
ucChannel, ucChannelType, ucNetworkNumber, ucExtend );
args[0] = channel;
args[1] = channel_type;
args[2] = network_number;
args[3] = extended_options;
return rb_class_new_instance( 4, args, rant_cAntChannel );
}
Close the USB connection to the ANT module.
static VALUE
rant_s_close( VALUE _module )
{
ANT_Close();
rant_channel_clear_registry();
return Qtrue;
}
Returns the serial number of the ANT device; not implemented on all devices.
static VALUE
rant_s_device_serial_number( VALUE _module )
{
#ifdef HAVE_ANT_GETDEVICESERIALNUMBER
const unsigned long serial = ANT_GetDeviceSerialNumber();
return LONG2FIX( serial );
#else
rb_notimplement();
#endif
}
Get the product and serial info of the USB device device_num
.
static VALUE
rant_s_device_usb_info( VALUE _module, VALUE device_num )
{
const unsigned short deviceNum = NUM2SHORT( device_num );
unsigned char product_string[256];
unsigned char serial_string[256];
VALUE rval = rb_ary_new2( 2 );
if ( !ANT_GetDeviceUSBInfo( (unsigned char)deviceNum, product_string, serial_string ) ) {
return Qnil;
}
rant_log_obj( _module, "debug", "Got product string = %s, serial string = %s", product_string, serial_string );
rb_ary_push( rval, rb_str_new_cstr((const char *)product_string) );
rb_ary_push( rval, rb_str_new_cstr((const char *)serial_string) );
return rval;
}
Returns the pid
of the USB device.
static VALUE
rant_s_device_usb_pid( VALUE _module )
{
unsigned short pid;
if ( !ANT_GetDeviceUSBPID(&pid) ) {
rb_sys_fail( "Fetching the USB PID." );
}
return INT2FIX( pid );
}
Returns the vid
of the USB device.
static VALUE
rant_s_device_usb_vid( VALUE _module )
{
unsigned short vid;
if ( !ANT_GetDeviceUSBVID(&vid) ) {
rb_sys_fail( "Fetching the USB VID." );
}
return INT2FIX( vid );
}
Initialize the ANT library and connect to the ANT module. The device_num
is the USB device number of the module to connect to, defaulting to 0. Modules connected to a PC will be assigned USB device numbers starting from 0. N is the number of USB ANT devices that are connected. The baud_rate
is the asynchronous baud rate used to connect to the ANT controller. See specific ANT controllers for allowable baud rates.
static VALUE
rant_s_init( int argc, VALUE *argv, VALUE _module )
{
VALUE device_num = Qnil, baud_rate = Qnil;
unsigned char ucUSBDeviceNum;
unsigned int ulBaudrate;
rb_scan_args( argc, argv, "02", &device_num, &baud_rate );
if ( RTEST(device_num) ) {
ucUSBDeviceNum = NUM2CHR( device_num );
} else {
ucUSBDeviceNum = 0;
}
if ( RTEST(baud_rate) ) {
ulBaudrate = NUM2UINT( baud_rate );
} else {
ulBaudrate = DEFAULT_BAUDRATE;
}
rant_log_obj( rant_mAnt, "info", "Initializing ANT device %d at %d baud", ucUSBDeviceNum, ulBaudrate );
if ( !ANT_Init(ucUSBDeviceNum, ulBaudrate) ) {
rb_raise( rb_eRuntimeError, "Initializing the ANT library (no ANT device present?)." );
}
return Qtrue;
}
Return the version of the underlying libant.
static VALUE
rant_s_lib_version( VALUE _module )
{
const char *version = ANT_LibVersion();
return rb_str_new_cstr( version );
}
Write debugging logs to the specified directory, which should already exist.
static VALUE
rant_s_log_directory_eq( VALUE _module, VALUE directory )
{
const char *directory_s = StringValueCStr( directory );
bool rval = ANT_SetDebugLogDirectory( (char *)directory_s );
return rval ? Qtrue : Qfalse;
}
Sets the response callback. The callback is called whenever a response message is received from ANT. See set_response_handlers for a set of default handlers.
static VALUE
rant_s_on_response( int argc, VALUE *argv, VALUE module )
{
VALUE callback = Qnil;
rb_scan_args( argc, argv, "0&", &callback );
if ( !RTEST(callback) ) {
rb_raise( rb_eLocalJumpError, "block required, but not given" );
}
rant_log( "debug", "Callback is: %s", RSTRING_PTR(rb_inspect(callback)) );
rb_ivar_set( module, response_callback_ivar, callback );
ANT_AssignResponseFunction( rant_on_response_callback, pucResponseBuffer );
return Qtrue;
}
Reset the system and put it in a known, low-power state. Execution of this command terminates all channels. All information previously configured in the system can no longer be considered valid.
static VALUE
rant_s_reset( VALUE _module )
{
const struct timeval wait_time = {
.tv_sec = 0,
.tv_usec = 500,
};
ANT_ResetSystem();
rant_channel_clear_registry();
// After a Reset System command has been issued, the application should wait
// 500ms to ensure that ANT is in the proper, “after-reset” state before any
// further commands are issued from the host.
rb_thread_wait_for( wait_time );
return Qtrue;
}
Configures a network address for use by one of the available network numbers.
static VALUE
rant_s_set_network_key( VALUE _module, VALUE network_number, VALUE key )
{
const unsigned short ucNetNumber = NUM2USHORT( network_number );
const char *pucKey = StringValuePtr( key );
if ( RSTRING_LEN(key) != 8 ) {
rb_raise( rb_eArgError, "expected an 8-byte key" );
}
if ( !ANT_SetNetworkKey(ucNetNumber, (unsigned char *)pucKey) ) {
rb_raise( rb_eRuntimeError, "could not set the network key." );
}
return Qtrue;
}
Set up the given mod
as the handler module for response callbacks. You can create your own handler module and extend with the default Ant::ResponseCallbacks
.
# File lib/ant.rb, line 46
def self::set_response_handlers( mod=Ant::ResponseCallbacks )
self.extend( mod )
self.on_response( &self.method(:handle_response_callback) )
end
Enable or disable extended Rx messages. If the device supports it, when ANT will include the channel ID with the data message.
static VALUE
rant_s_use_extended_messages_eq( VALUE _module, VALUE true_false )
{
// This is documented as an unsigned char and then explicitly cast
// to a signed char. So this just uses their typedef.
const BOOL ucEnable = RTEST( true_false ) ? TRUE : FALSE;
rant_log( "info", "%s extended messages.", ucEnable ? "Enabling" : "Disabling" );
ANT_RxExtMesgsEnable( ucEnable );
return Qtrue;
}
Check that specified frequency
is a valid channel period and raise an appropriate exception if it isn't. Returns the frequency as an Integer if it is valid.
# File lib/ant.rb, line 83
def self::validate_channel_period( frequency )
frequency = Integer( frequency )
unless VALID_CHANNEL_PERIODS.include?( frequency )
raise RangeError, "invalid channel period; expected a frequency between %d and %d, got %p" %
[ VALID_CHANNEL_PERIODS.begin, VALID_CHANNEL_PERIODS.end, frequency ]
end
return frequency
end
Check that specified number
is a valid device number and raise an appropriate exception if it isn't. Returns the number as an Integer if it is valid.
# File lib/ant.rb, line 55
def self::validate_device_number( number )
number = Integer( number )
unless VALID_DEVICE_NUMBERS.include?( number )
raise RangeError, "invalid device number; expected a number between %d and %d, got %p" %
[ VALID_DEVICE_NUMBERS.begin, VALID_DEVICE_NUMBERS.end, number ]
end
return number
end
Check that specified number
is a valid device type and raise an appropriate exception if it isn't. Returns the number as an Integer if it is valid.
# File lib/ant.rb, line 69
def self::validate_device_type( number )
number = Integer( number )
unless VALID_DEVICE_TYPES.include?( number )
raise RangeError, "invalid device type; expected a number between %d and %d, got %p" %
[ VALID_DEVICE_TYPES.begin, VALID_DEVICE_TYPES.end, number ]
end
return number
end
Check that specified data
is a valid ANT network key and raise an appropriate exception if it isn't. Returns the key itself if it is valid.
# File lib/ant.rb, line 111
def self::validate_network_key( data )
data = data.to_s
unless data.bytesize == 8
raise RangeError, "invalid network key; expected exactly eight bytes, got %d" %
[ data.bytesize ]
end
self.log.debug "Validated network key: %p" % [ data ]
return data
end
Check that specified number
is a valid ANT network number and raise an appropriate exception if it isn't. Note that this does not check the local device(s) to ensure they support the given network. Returns the key as an Integer if it is valid.
# File lib/ant.rb, line 98
def self::validate_network_number( number )
number = Integer( number )
unless number >= 0 && number <= 255
raise RangeError, "invalid network number; expected an eight-bit number, got %p" %
[ number ]
end
return number
end
Check that specified offset
is a valid “rf frequency” and raise an appropriate exception if it isn't. Returns the offset as an Integer if it is valid.
# File lib/ant.rb, line 126
def self::validate_rf_frequency( offset )
offset = Integer( offset )
unless VALID_RF_FREQUENCIES.include?( offset )
raise RangeError, "invalid RF Frequency; expected a offset between %d and %d, got %p" %
[ VALID_RF_FREQUENCIES.begin, VALID_RF_FREQUENCIES.end, offset ]
end
return offset
end