예제 #1
0
def main():
    # logging.basicConfig()

    monitor = Monitor()

    node = Node()
    node.set_network_key(0x00, NETWORK_KEY)

    channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)

    channel.on_broadcast_data = monitor.on_data_heartrate
    channel.on_burst_data = monitor.on_data_heartrate

    channel.set_period(8070)
    channel.set_search_timeout(12)
    channel.set_rf_freq(57)
    channel.set_id(0, 120, 0)

    channel_cadence_speed = node.new_channel(
        Channel.Type.BIDIRECTIONAL_RECEIVE)

    channel_cadence_speed.on_broadcast_data = monitor.on_data_cadence_speed
    channel_cadence_speed.on_burst_data = monitor.on_data_cadence_speed

    channel_cadence_speed.set_period(8085)
    channel_cadence_speed.set_search_timeout(30)
    channel_cadence_speed.set_rf_freq(57)
    channel_cadence_speed.set_id(0, 121, 0)

    try:
        channel.open()
        channel_cadence_speed.open()
        node.start()
    finally:
        node.stop()
예제 #2
0
def main():
    print("ANT+ Open Rx Scan Mode Demo")
    logging.basicConfig(filename="example.log", level=logging.DEBUG)

    TimeProgramStart = time.time()  # get start time

    node = Node()
    node.set_network_key(0x00, NETWORK_KEY)  # 1. Set Network Key
    # CHANNEL CONFIGURATION
    channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE, 0x00,
                               0x00)  # 2. Assign channel

    channel.on_broadcast_data = on_data_scan
    channel.on_burst_data = on_data_scan
    channel.on_acknowledge = on_data_scan
    channel.on_acknowledge_data = on_data_ack_scan  # von mir

    channel.set_id(0, 0, 0)  # 3. Set Channel ID
    channel.set_period(0)  # 4. Set Channel Period
    channel.set_rf_freq(57)  # 5. Set RadioFrequenzy
    channel.enable_extended_messages(
        1)  # 6. Enable Extended Messages, needed for OpenRxScanMode

    try:
        channel.open_rx_scan_mode()  #  7. OpenRxScanMode
        node.start()
    except KeyboardInterrupt:
        print("Closing ANT+ Channel")
        channel.close()
        node.stop()
    finally:
        node.stop()
        logging.shutdown()  # Shutdown Logger
예제 #3
0
def main():
    fan.gearOff()
    logging.info("Setting:")
    i = 0
    while i < len(fan.pinlist):
        info = "*  gear " + str(i + 1) + " if heartrate greater " + str(
            myHeartrateLevel[i])
        i += 1
        logging.info(info)
    # ant device
    node = Node()
    node.set_network_key(0x00, NETWORK_KEY)
    channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
    # define the callback
    channel.on_broadcast_data = on_data
    channel.on_burst_data = on_data
    #
    channel.set_period(8070)
    channel.set_search_timeout(12)
    channel.set_rf_freq(57)
    channel.set_id(0, 120, 0)

    try:
        channel.open()
        node.start()
    finally:
        fan.gearOff()
        node.stop()
        GPIO.cleanup()
예제 #4
0
    def __init__(self):
        self.count = 0

        logging.basicConfig(filename="example.log", level=logging.DEBUG)

        node = Node()
        node.set_network_key(0x00, NETWORK_KEY)

        # Master channel configuration for Generic Control Device
        self.channel = node.new_channel(Channel.Type.BIDIRECTIONAL_TRANSMIT)
        self.channel.set_id(1, 16, 5)
        self.channel.set_period(8192)
        self.channel.set_rf_freq(57)

        # Callbacks
        self.channel.on_broadcast_tx_data = self.on_tx_data
        self.channel.on_acknowledge_data = self.on_acknowledge_data
        self.channel.on_broadcast_data = self.on_broadcast_data

        try:
            print("Opening ANT+ Channel ...")
            self.channel.open()
            node.start()
        except KeyboardInterrupt:
            print("Closing ANT+ Channel ...")
            self.channel.close()
            node.stop()
        finally:
            logging.shutdown()
예제 #5
0
파일: scan.py 프로젝트: rdeterre/openant
def main():
    #logging.basicConfig(filename='example.log',level=logging.DEBUG)

    node = Node()
    node.set_network_key(0x00, NETWORK_KEY)

    channel_scan = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE,0x00,0x01)

    channel_scan.on_broadcast_data = scan_data
    channel_scan.on_burst_data = scan_data
    channel_scan.on_acknowledge = scan_data

    channel_scan.set_id(0, 120, 0)
    channel_scan.enable_extended_messages(1)
    channel_scan.set_search_timeout(0xFF)
    channel_scan.set_period(8070)
    channel_scan.set_rf_freq(57)


    channel_hrm = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)

    channel_hrm.on_broadcast_data = hrm_data
    channel_hrm.on_burst_data = hrm_data
    channel_hrm.on_acknowledge = hrm_data

    channel_hrm.set_id(49024, 120, 0)
    channel_hrm.enable_extended_messages(1)
    channel_hrm.set_search_timeout(0xFF)
    channel_hrm.set_period(32280)
    channel_hrm.set_rf_freq(57)

    channel_hrm2 = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)

    channel_hrm2.on_broadcast_data = hrm_data
    channel_hrm2.on_burst_data = hrm_data
    channel_hrm2.on_acknowledge = hrm_data

    channel_hrm2.set_id(25170, 120, 0)
    channel_hrm2.enable_extended_messages(1)
    channel_hrm2.set_search_timeout(0xFF)
    channel_hrm2.set_period(32280)
    channel_hrm2.set_rf_freq(57)
    try:
        channel_scan.open()
        time.sleep(10)
        channel_scan.close()
        channel_scan._unassign()
        channel_hrm.open()
        channel_hrm2.open()
        #channel_scan.open()
        node.start()
    finally:
        channel_hrm.close()
        channel_hrm._unassign()
        channel_hrm2.close()
        channel_hrm2._unassign()
        node.stop()
예제 #6
0
파일: test.py 프로젝트: pirower/test1
class AntEasyTests(unittest.TestCase):
    @unittest.skipUnless("ANT_TEST_USB_STICK" in os.environ,
                         "Testing with USB stick not enabled")
    def test_search(self):

        try:
            logger = logging.getLogger("ant")
            logger.setLevel(logging.DEBUG)
            handler = logging.StreamHandler()
            handler.setFormatter(
                logging.Formatter(
                    fmt="%(asctime)s  %(name)-15s  %(levelname)-8s  %(message)s"
                ))
            logger.addHandler(handler)

            self.node = Node()
            print("Request basic information...")
            m = self.node.request_message(Message.ID.RESPONSE_ANT_VERSION)
            print("  ANT version:  ", struct.unpack("<10sx", m[2])[0])
            m = self.node.request_message(Message.ID.RESPONSE_CAPABILITIES)
            print("  Capabilities: ", m[2])
            m = self.node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER)
            print("  Serial number:", struct.unpack("<I", m[2])[0])

            print("Starting system...")

            NETWORK_KEY = [0xA8, 0xA4, 0x23, 0xB9, 0xF5, 0x5E, 0x63, 0xC1]

            # self.node.reset_system()
            self.node.set_network_key(0x00, NETWORK_KEY)

            c = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)

            c.set_period(4096)
            c.set_search_timeout(255)
            c.set_rf_freq(50)
            c.set_search_waveform([0x53, 0x00])
            c.set_id(0, 0x01, 0)

            print("Open channel...")
            c.open()
            c.request_message(Message.ID.RESPONSE_CHANNEL_STATUS)

            print("Searching...")

            self.node.start()

            print("Done")
        except KeyboardInterrupt:
            print("Interrupted")
            self.node.stop()
            sys.exit(1)

    def stop(self):
        self.node.stop()
예제 #7
0
파일: test.py 프로젝트: Tigge/openant
class AntEasyTests(unittest.TestCase):
    @unittest.skipUnless("ANT_TEST_USB_STICK" in os.environ, "Testing with USB stick not enabled")
    def test_search(self):

        try:
            logger = logging.getLogger("ant")
            logger.setLevel(logging.DEBUG)
            handler = logging.StreamHandler()
            handler.setFormatter(logging.Formatter(fmt='%(asctime)s  %(name)-15s  %(levelname)-8s  %(message)s'))
            logger.addHandler(handler)

            self.node = Node()
            print("Request basic information...")
            m = self.node.request_message(Message.ID.RESPONSE_ANT_VERSION)
            print("  ANT version:  ", struct.unpack("<10sx", m[2])[0])
            m = self.node.request_message(Message.ID.RESPONSE_CAPABILITIES)
            print("  Capabilities: ", m[2])
            m = self.node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER)
            print("  Serial number:", struct.unpack("<I", m[2])[0])

            print("Starting system...")

            NETWORK_KEY = [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1]

            # self.node.reset_system()
            self.node.set_network_key(0x00, NETWORK_KEY)

            c = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)

            c.set_period(4096)
            c.set_search_timeout(255)
            c.set_rf_freq(50)
            c.set_search_waveform([0x53, 0x00])
            c.set_id(0, 0x01, 0)

            print("Open channel...")
            c.open()
            c.request_message(Message.ID.RESPONSE_CHANNEL_STATUS)

            print("Searching...")

            self.node.start()

            print("Done")
        except KeyboardInterrupt:
            print("Interrupted")
            self.node.stop()
            sys.exit(1)

    def stop(self):
        self.node.stop()
예제 #8
0
def main():
    def on_data(data):
        heartrate = data[7]
        string = "Heartrate: " + str(heartrate) + " [BPM]"
        
        sys.stdout.write(string)
        sys.stdout.flush()
        sys.stdout.write("\b" * len(string))
        if len(data) > 8:
            print(data)
            deviceNumberLSB = data[9]
            deviceNumberMSB = data[10]
            deviceNumber = "{}".format(deviceNumberLSB + (deviceNumberMSB << 8))
            deviceType = "{}".format(data[11])
            print("New Device Found: %s of type %s" % (deviceNumber, deviceType))
            result.append(deviceNumber)

    logging.basicConfig(filename="example.log", level=logging.DEBUG)

    result = []

    node = Node()
    node.set_network_key(0x00, NETWORK_KEY)

    print(node.ant._driver._out)

    channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE, 0x00, 0x01)

    channel.on_broadcast_data = on_data
    channel.on_burst_data = on_data
    channel.on_acknowledge = on_data

    channel.set_id(0, 120, 0)
    channel.enable_extended_messages(1)
    channel.set_search_timeout(0xFF)
    channel.set_period(8070)
    channel.set_rf_freq(57)

    try:
        node.start()
        channel.open()
        time.sleep(10)
        channel.close()
        time.sleep(0.5) #just to give the event handler to treat incoming message before unassigning it
        channel._unassign()
        result = list(set(result))
        result.sort()
        print("New Devices Found: \n %s " % (result))
    finally:
        node.stop()
예제 #9
0
def main():
    # logging.basicConfig()

    node = Node()
    node.set_network_key(0x00, NETWORK_KEY)

    channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)

    channel.on_broadcast_data = on_data
    channel.on_burst_data = on_data

    channel.set_period(8070)
    channel.set_search_timeout(12)
    channel.set_rf_freq(57)
    channel.set_id(0, 120, 0)

    try:
        channel.open()
        node.start()
    finally:
        node.stop()
예제 #10
0
def main():
    # logging.basicConfig()

    node = Node()
    node.set_network_key(0x00, NETWORK_KEY)

    channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)

    channel.on_broadcast_data = on_data
    channel.on_burst_data = on_data

    channel.set_period(8070)
    channel.set_search_timeout(12)
    channel.set_rf_freq(57)
    channel.set_id(0, 120, 0)

    try:
        channel.open()
        node.start()
    finally:
        node.stop()
예제 #11
0
def main():
    node = Node()
    node.set_network_key(0x00, NETWORK_KEY)

    channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)

    channel.on_broadcast_data = on_data
    channel.on_burst_data = on_data

    channel.set_period(8070)
    channel.set_search_timeout(12)
    channel.set_rf_freq(57)
    channel.set_id(0, 120, 0)

    try:
        channel.open()
        node.start()

    finally:
        node.stop()
        GPIO.cleanup()
        colorWipe(strip, Color(0, 0, 0), 10)
예제 #12
0
파일: scan.py 프로젝트: pwithnall/openant
def main():
    logging.basicConfig(filename='example.log', level=logging.DEBUG)

    node = Node()
    node.set_network_key(0x00, NETWORK_KEY)

    channel = node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE, 0x00, 0x01)

    channel.on_broadcast_data = on_data
    channel.on_burst_data = on_data
    channel.on_acknowledge = on_data

    channel.set_id(0, 120, 0)
    channel.enable_extended_messages(1)
    channel.set_search_timeout(0xFF)
    channel.set_period(8070)
    channel.set_rf_freq(57)

    try:
        channel.open()
        node.start()
    finally:
        node.stop()
예제 #13
0
파일: manager.py 프로젝트: La0/GFrun
class Application:

    _serial_number = 1337
    _frequency = 19  # 0 to 124, x - 2400 (in MHz)

    def __init__(self):

        self._queue = Queue.Queue()
        self._beacons = Queue.Queue()

        self._node = Node(0x0fcf, 0x1008)

        print "Request basic information..."
        m = self._node.request_message(Message.ID.RESPONSE_VERSION)
        print "  ANT version:  ", struct.unpack("<10sx", m[2])[0]
        m = self._node.request_message(Message.ID.RESPONSE_CAPABILITIES)
        print "  Capabilities: ", m[2]
        m = self._node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER)
        print "  Serial number:", struct.unpack("<I", m[2])[0]

        print "Starting system..."

        NETWORK_KEY = [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1]

        self._node.reset_system()
        self._node.set_network_key(0x00, NETWORK_KEY)

        self._channel = self._node.new_channel(
            Channel.Type.BIDIRECTIONAL_RECEIVE)
        self._channel.on_broadcast_data = self._on_data
        self._channel.on_burst_data = self._on_data

        self.setup_channel(self._channel)

        self._worker_thread = threading.Thread(target=self._worker,
                                               name="ant.fs")
        self._worker_thread.start()

    def _worker(self):
        self._node.start()

    def _main(self):
        try:
            _logger.debug("Link level")
            beacon = self._get_beacon()
            if self.on_link(beacon):
                for i in range(0, 5):
                    beacon = self._get_beacon()
                    if beacon.get_client_device_state(
                    ) == Beacon.ClientDeviceState.AUTHENTICATION:
                        _logger.debug("Auth layer")
                        if self.on_authentication(beacon):
                            _logger.debug("Authenticated")
                            beacon = self._get_beacon()
                            self.on_transport(beacon)
                        self.disconnect()
                        break
        except Exception as e:
            print e
            for line in traceback.format_exc().splitlines():
                _logger.error("%r", line)
        finally:
            _logger.debug("Run 5")
            self.stop()

    def _on_beacon(self, data):
        b = Beacon.parse(data)
        self._beacons.put(b)

    def _on_command(self, data):
        c = ant.fs.command.parse(data)
        self._queue.put(c)

    def _on_data(self, data):
        #print "_on_data", data, len(data)
        if data[0] == 0x43:
            self._on_beacon(data[:8])
            if len(data[8:]) > 0:
                self._on_command(data[8:])
        elif data[0] == 0x44:
            self._on_command(data)

    def _get_beacon(self):
        b = self._beacons.get()
        self._beacons.task_done()
        return b

    def _get_command(self, timeout=3.0):
        _logger.debug("Get command, t%d, s%d", timeout, self._queue.qsize())
        c = self._queue.get(True, timeout)
        self._queue.task_done()
        return c

    def _send_command(self, c):
        data = c.get()
        if len(data) == 8:
            self._channel.send_acknowledged_data(data)
        else:
            self._channel.send_burst_transfer(data)

    # Application actions are defined from here
    # =======================================================================

    # These should be overloaded:

    def setup_channel(self, channel):
        pass

    def on_link(self, beacon):
        pass

    def on_authentication(self, beacon):
        pass

    def on_transport(self, beacon):
        pass

    # Shouldn't have to touch these:

    def start(self):
        self._main()

    def stop(self):
        self._node.stop()

    def erase(self, index):
        pass

    def upload(self, index, data):
        pass

    def download(self, index, callback=None):
        offset = 0
        initial = True
        crc = 0
        data = array.array('B')
        while True:
            _logger.debug("Download %d, o%d, c%d", index, offset, crc)
            self._send_command(DownloadRequest(index, offset, True, crc))
            _logger.debug("Wait for response...")
            try:
                response = self._get_command()
                if response._get_argument(
                        "response") == DownloadResponse.Response.OK:
                    remaining = response._get_argument("remaining")
                    offset = response._get_argument("offset")
                    total = offset + remaining
                    data[offset:total] = response._get_argument(
                        "data")[:remaining]
                    #print "rem", remaining, "offset", offset, "total", total, "size", response._get_argument("size")
                    if callback != None:
                        callback(
                            float(total) /
                            float(response._get_argument("size")))
                    if total == response._get_argument("size"):
                        return data
                    crc = response._get_argument("crc")
                    offset = total
                else:
                    raise AntFSDownloadException(
                        response._get_argument("response"))
            except Queue.Empty:
                _logger.debug("Download %d timeout", index)
                #print "recover from download failure"

    def download_directory(self, callback=None):
        data = self.download(0, callback)
        return Directory.parse(data)

    def link(self):
        self._channel.request_message(Message.ID.RESPONSE_CHANNEL_ID)
        self._send_command(LinkCommand(self._frequency, 4,
                                       self._serial_number))

        # New period, search timeout
        self._channel.set_period(4096)
        self._channel.set_search_timeout(3)
        self._channel.set_rf_freq(self._frequency)

    def authentication_serial(self):
        self._send_command(
            AuthenticateCommand(AuthenticateCommand.Request.SERIAL,
                                self._serial_number))
        response = self._get_command()
        return (response.get_serial(), response.get_data_string())

    def authentication_passkey(self, passkey):
        self._send_command(
            AuthenticateCommand(AuthenticateCommand.Request.PASSKEY_EXCHANGE,
                                self._serial_number, passkey))

        response = self._get_command()
        if response._get_argument(
                "type") == AuthenticateResponse.Response.ACCEPT:
            return response.get_data_array()
        else:
            raise AntFSAuthenticationException(response._get_argument("type"))

    def authentication_pair(self, friendly_name):
        data = array.array('B', map(ord, list(friendly_name)))
        self._send_command(
            AuthenticateCommand(AuthenticateCommand.Request.PAIRING,
                                self._serial_number, data))

        response = self._get_command(30)
        if response._get_argument(
                "type") == AuthenticateResponse.Response.ACCEPT:
            return response.get_data_array()
        else:
            raise AntFSAuthenticationException(response._get_argument("type"))

    def disconnect(self):
        d = DisconnectCommand(DisconnectCommand.Type.RETURN_LINK, 0, 0)
        self._send_command(d)
예제 #14
0
class AntPlusNode:
    def __init__(self, network_key):
        self.node = Node()
        self.node.set_network_key(0x00, network_key)
        self.ant_device_id = 1

    def attach_hrm(self, device_number=0, transfer_type=0):
        channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        hrm = AntPlusHRM(channel,
                         device_number=device_number,
                         transfer_type=transfer_type)
        channel.open()
        return hrm

    def attach_power_meter(self, device_number=0, transfer_type=0):
        channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        pwr_meter = AntPlusPowerMeter(channel,
                                      device_number=device_number,
                                      transfer_type=transfer_type)
        pwr_meter._ant_device_id = self.check_id_of_power_meter(
        )  #id checked once at creation of pwr_meter
        channel.open()
        return pwr_meter

    def attach_speed_sensor(self,
                            wheel_circumference_meters,
                            device_number=0,
                            transfer_type=0):
        channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        sensor = AntPlusSpeedSensor(
            channel,
            wheel_circumference_meters=wheel_circumference_meters,
            device_number=device_number,
            transfer_type=transfer_type)
        channel.open()
        return sensor

    def attach_cadence_sensor(self, device_number=0, transfer_type=0):
        channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        sensor = AntPlusCadenceSensor(channel,
                                      device_number=device_number,
                                      transfer_type=transfer_type)
        channel.open()
        return sensor

    def attach_speed_and_cadence_sensor(self,
                                        wheel_circumference_meters,
                                        device_number=0,
                                        transfer_type=0):
        channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        sensor = AntPlusSpeedAndCadenceSensor(
            channel,
            wheel_circumference_meters=wheel_circumference_meters,
            device_number=device_number,
            transfer_type=transfer_type)
        channel.open()
        return sensor

    def attach_combined_speed_and_cadence_sensor(self,
                                                 wheel_circumference_meters,
                                                 speed_device_number=0,
                                                 speed_transfer_type=0,
                                                 cadence_device_number=0,
                                                 cadence_transfer_type=0):
        channel1 = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        channel2 = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        sensor = AntPlusCombinedSpeedAndCadenceSensors(
            channel1,
            channel2,
            wheel_circumference_meters=wheel_circumference_meters,
            speed_device_number=speed_device_number,
            speed_transfer_type=speed_transfer_type,
            cadence_device_number=cadence_device_number,
            cadence_transfer_type=cadence_transfer_type)
        channel1.open()
        channel2.open()
        return sensor

    def check_id_of_power_meter(self, device_number=0, transfer_type=0):
        meter_id = self.node.request_message(Message.ID.RESPONSE_CHANNEL_ID)
        return meter_id

    def start(self):
        self.node.start()

    def stop(self):
        self.node.stop()
class Application:
    
    _serial_number = 1337
    _frequency     = 19    # 0 to 124, x - 2400 (in MHz)
    
    def __init__(self):

        self._queue = Queue.Queue()
        self._beacons = Queue.Queue()

        self._node = Node()

        try:
            NETWORK_KEY= [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1]
            self._node.set_network_key(0x00, NETWORK_KEY)


            print "Request basic information..."

            m = self._node.request_message(Message.ID.RESPONSE_CAPABILITIES)
            print "  Capabilities: ", m[2]

            #m = self._node.request_message(Message.ID.RESPONSE_VERSION)
            #print "  ANT version:  ", struct.unpack("<10sx", m[2])[0]

            #m = self._node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER)
            #print "  Serial number:", struct.unpack("<I", m[2])[0]

            print "Starting system..."

            #NETWORK_KEY= [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1]
            #self._node.set_network_key(0x00, NETWORK_KEY)

            print "Key done..."

            self._channel = self._node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
            self._channel.on_broadcast_data = self._on_data
            self._channel.on_burst_data = self._on_data
            
            self.setup_channel(self._channel)
            
            self._worker_thread =threading.Thread(target=self._worker, name="ant.fs")
            self._worker_thread.start()
        except Exception as e:
            self.stop()
            raise e

    def _worker(self):
        self._node.start()

    def _main(self):
        try:
            _logger.debug("Link level")
            beacon = self._get_beacon()
            if self.on_link(beacon):
                for i in range(0, 5):
                    beacon = self._get_beacon()
                    if beacon.get_client_device_state() == Beacon.ClientDeviceState.AUTHENTICATION:
                        _logger.debug("Auth layer")
                        if self.on_authentication(beacon):
                            _logger.debug("Authenticated")
                            beacon = self._get_beacon()
                            self.on_transport(beacon)
                        self.disconnect()
                        break
        #except Exception as e:
        #    print e
        #    traceback.print_exc()
        #    for line in traceback.format_exc().splitlines():
        #        _logger.error("%r", line)
        finally:
            _logger.debug("Run 5")
            self.stop()


    def _on_beacon(self, data):
        b = Beacon.parse(data)
        self._beacons.put(b)

    def _on_command(self, data):
        c = ant.fs.command.parse(data)
        self._queue.put(c)

    def _on_data(self, data):
        #print "_on_data", data, len(data)
        if data[0] == 0x43:
            self._on_beacon(data[:8])
            if len(data[8:]) > 0:
                self._on_command(data[8:])
        elif data[0] == 0x44:
            self._on_command(data)
    
    def _get_beacon(self):
        b = self._beacons.get()
        self._beacons.task_done()
        return b
    
    def _get_command(self, timeout=3.0):
        _logger.debug("Get command, t%d, s%d", timeout, self._queue.qsize())
        c = self._queue.get(True, timeout)
        self._queue.task_done()
        return c
    
    def _send_command(self, c):
        data = c.get()
        if len(data) == 8:
            self._channel.send_acknowledged_data(data)
        else:
            self._channel.send_burst_transfer(data)
    
    # Application actions are defined from here
    # =======================================================================
    
    # These should be overloaded:
    
    def setup_channel(self, channel):
        pass
    
    def on_link(self, beacon):
        pass
    
    def on_authentication(self, beacon):
        pass
    
    def on_transport(self, beacon):
        pass
    
    # Shouldn't have to touch these:
    
    def start(self):
        self._main()

    def stop(self):
        self._node.stop()
    
    def erase(self, index):
        pass
    
    def _send_commandpipe(self, data):
        #print "send commandpipe", data
        self.upload(0xfffe, data)
    
    def _get_commandpipe(self):
        #print "get commandpipe"
        return ant.fs.commandpipe.parse(self.download(0xfffe))
    
    def create(self, typ, data, callback=None):
        #print "create", typ
        request = CreateFile(len(data), 0x80, [typ, 0x00, 0x00], [0x00, 0xff, 0xff])
        self._send_commandpipe(request.get())
        result = self._get_commandpipe()
        #result._debug()
        
        if result.get_response() != Response.Response.OK:
            raise AntFSCreateFileException("Could not create file",
                    result.get_response())
        
        #print "create result", result, result.get_index(), result.get_data_type(), result.get_identifier()
        #d = self.download_directory()
        
        self.upload(result.get_index(), data, callback)
        return result.get_index()
    
    def upload(self, index, data, callback=None):
        #print "upload", index, len(data)

        iteration = 0
        while True:
            
            # Request Upload
            
            # Continue using Last Data Offset (special MAX_ULONG value)
            request_offset = 0 if iteration == 0 else 0xffffffff
            self._send_command(UploadRequest(index, len(data), request_offset))
            
            upload_response = self._get_command()
            #upload_response._debug()
            
            if upload_response._get_argument("response") != UploadResponse.Response.OK:
                raise AntFSUploadException("Upload request failed",
                        upload_response._get_argument("response"))

            # Upload data
            offset      = upload_response._get_argument("last_data_offset")
            max_block   = upload_response._get_argument("maximum_block_size")
            #print " uploading", offset, "to", offset + max_block
            data_packet = data[offset:offset + max_block]
            crc_seed    = upload_response._get_argument("crc")
            crc_val     = crc(data_packet, upload_response._get_argument("crc"))
            
            # Pad with 0 to even 8 bytes
            missing_bytes = 8 - (len(data_packet) % 8)
            if missing_bytes != 8:
                data_packet.extend(array.array('B', [0] * missing_bytes))
                #print " adding", str(missing_bytes), "padding"

            #print " packet", len(data_packet)
            #print " crc   ", crc_val, "from seed", crc_seed

            self._send_command(UploadDataCommand(crc_seed, offset, data_packet, crc_val))
            upload_data_response = self._get_command()
            #upload_data_response._debug()
            if upload_data_response._get_argument("response") != UploadDataResponse.Response.OK:
                raise AntFSUploadException("Upload data failed",
                        upload_data_response._get_argument("response"))
            
            if callback != None and len(data) != 0:
                callback(float(offset) / float(len(data)))

            if offset + len(data_packet) >= len(data):
                #print " done"
                break

            #print " one more"
            iteration += 1

    
    def download(self, index, callback=None):
        offset  = 0
        initial = True
        crc     = 0
        data    = array.array('B')
        while True:
            _logger.debug("Download %d, o%d, c%d", index, offset, crc)
            self._send_command(DownloadRequest(index, offset, True, crc))
            _logger.debug("Wait for response...")
            try:
                response = self._get_command()
                resp = response._get_argument("response")
                if resp == DownloadResponse.Response.OK:
                    remaining    = response._get_argument("remaining")
                    offset       = response._get_argument("offset")
                    total        = offset + remaining
                    data[offset:total] = response._get_argument("data")[:remaining]
                    #print "rem", remaining, "offset", offset, "total", total, "size", response._get_argument("size")
                    # TODO: check CRC
                    
                    if callback != None and response._get_argument("size") != 0:
                        callback(float(total) / float(response._get_argument("size")))
                    if total == response._get_argument("size"):
                        return data
                    crc = response._get_argument("crc")
                    offset = total
                elif resp == DownloadResponse.Response.NOT_READABLE:
                    return data
                else:
                    raise AntFSDownloadException("Download request failed: ",
                            response._get_argument("response"))
            except Queue.Empty:
                _logger.debug("Download %d timeout", index)
                #print "recover from download failure"
    
    def download_directory(self, callback=None):
        data = self.download(0, callback)
        return Directory.parse(data)

    def link(self):
        self._channel.request_message(Message.ID.RESPONSE_CHANNEL_ID)
        self._send_command(LinkCommand(self._frequency, 4, self._serial_number))
       
        # New period, search timeout
        self._channel.set_period(4096)
        self._channel.set_search_timeout(3)
        self._channel.set_rf_freq(self._frequency)

    def authentication_serial(self):
        self._send_command(AuthenticateCommand(
                AuthenticateCommand.Request.SERIAL,
                self._serial_number))
        response = self._get_command()
        return (response.get_serial(), response.get_data_string())

    def authentication_passkey(self, passkey):
        self._send_command(AuthenticateCommand(
                AuthenticateCommand.Request.PASSKEY_EXCHANGE,
                self._serial_number, passkey))

        response = self._get_command()
        if response._get_argument("type") == AuthenticateResponse.Response.ACCEPT:
            return response.get_data_array()
        else:
            raise AntFSAuthenticationException("Passkey authentication failed",
                    response._get_argument("type"))

    def authentication_pair(self, friendly_name):
        data = array.array('B', map(ord, list(friendly_name)))
        self._send_command(AuthenticateCommand(
                AuthenticateCommand.Request.PAIRING,
                self._serial_number, data))

        response = self._get_command(30)
        if response._get_argument("type") == AuthenticateResponse.Response.ACCEPT:
            return response.get_data_array()
        else:
            raise AntFSAuthenticationException("Pair authentication failed",
                    response._get_argument("type"))
        

    def disconnect(self):
        d = DisconnectCommand(DisconnectCommand.Type.RETURN_LINK, 0, 0)
        self._send_command(d)
예제 #16
0
class SensorANT(Sensor):

    #for openant
    node = None
    NETWORK_KEY = [0xb9, 0xa5, 0x21, 0xfb, 0xbd, 0x72, 0xc3, 0x45]
    NETWORK_NUM = 0x00
    CHANNEL = 0x00
    DEVICE_ALL = 0
    scanner = None
    device = {}

    def sensor_init(self):

        if not self.config.G_ANT['STATUS']:
            global _SENSOR_ANT
            _SENSOR_ANT = False

        if _SENSOR_ANT:
            self.node = Node()
            self.node.set_network_key(self.NETWORK_NUM, self.NETWORK_KEY)

        #initialize scan channel (reserve ch0)
        self.scanner = ANT_Device_MultiScan(self.node, self.config)
        self.searcher = ANT_Device_Search(self.node, self.config, self.values)
        self.scanner.setMainAntDevice(self.device)

        #auto connect ANT+ sensor from setting.conf
        if _SENSOR_ANT and not self.config.G_DUMMY_OUTPUT:
            for key in ['HR', 'SPD', 'CDC', 'PWR']:
                if self.config.G_ANT['USE'][key]:
                    antID = self.config.G_ANT['ID'][key]
                    antType = self.config.G_ANT['TYPE'][key]
                    self.connectAntSensor(key, antID, antType, False)
            return
        #otherwise, initialize
        else:
            for key in ['HR', 'SPD', 'CDC', 'PWR']:
                self.config.G_ANT['USE'][key] = False
                self.config.G_ANT['ID'][key] = 0
                self.config.G_ANT['TYPE'][key] = 0

        #for dummy output
        if not _SENSOR_ANT and self.config.G_DUMMY_OUTPUT:
            #need to set dummy ANT+ device id 0
            self.config.G_ANT['USE'] = {
                'HR': True,
                'SPD': True,
                'CDC': True,  #same as SPD
                'PWR': True,
            }
            self.config.G_ANT['ID_TYPE'] = {
                'HR': struct.pack('<HB', 0, 0x78),
                'SPD': struct.pack('<HB', 0, 0x79),
                'CDC': struct.pack('<HB', 0, 0x79),  #same as SPD
                'PWR': struct.pack('<HB', 0, 0x0B),
            }
            self.config.G_ANT['TYPE'] = {
                'HR': 0x78,
                'SPD': 0x79,
                'CDC': 0x79,  #same as SPD
                'PWR': 0x0B,
            }
            ac = self.config.G_ANT['ID_TYPE']
            self.values[ac['HR']] = {}
            self.values[ac['SPD']] = {'distance': 0}
            self.values[ac['PWR']] = {}
            for key in [0x10, 0x11, 0x12]:
                self.values[ac['PWR']][key] = {'accumulated_power': 0}

    def start(self):
        if _SENSOR_ANT:
            self.node.start()

    def update(self):
        if _SENSOR_ANT or not self.config.G_DUMMY_OUTPUT:
            return

        hr_value = random.randint(70, 130)
        speed_value = random.randint(5, 30) / 3.6  #5 - 30km/h [unit:m/s]
        cad_value = random.randint(0, 80)
        power_value = random.randint(0, 250)
        timestamp = datetime.datetime.now()
        if 0 < timestamp.second % 60 < 10:
            hr_value = speed_value = cad_value = power_value = self.config.G_ANT_NULLVALUE
        if 8 < timestamp.second % 60 < 10:
            speed_value = cad_value = power_value = 0

        ac = self.config.G_ANT['ID_TYPE']
        self.values[ac['HR']]['hr'] = hr_value
        self.values[ac['SPD']]['speed'] = speed_value
        self.values[ac['CDC']]['cadence'] = cad_value
        self.values[ac['PWR']][0x10]['power'] = power_value

        #TIMESTAMP
        self.values[ac['HR']]['timestamp'] = timestamp
        self.values[ac['SPD']]['timestamp'] = timestamp
        self.values[ac['PWR']][0x10]['timestamp'] = timestamp
        #DISTANCE, TOTAL_WORK
        if self.config.G_MANUAL_STATUS == "START":
            #DISTANCE: unit: m
            if not np.isnan(self.values[ac['SPD']]['speed']):
                self.values[ac['SPD']]['distance'] += \
                  self.values[ac['SPD']]['speed']*self.config.G_SENSOR_INTERVAL
            #TOTAL_WORK: unit: j
            if not np.isnan(self.values[ac['PWR']][0x10]['power']):
                self.values[ac['PWR']][0x10]['accumulated_power'] += \
                  self.values[ac['PWR']][0x10]['power']*self.config.G_SENSOR_INTERVAL

    def reset(self):
        for dv in self.device.values():
            dv.resetValue()

    def quit(self):
        if not _SENSOR_ANT:
            return
        self.node.ant.set_wait_action()
        #stop scanner and searcher
        if not self.scanner.stop():
            for dv in self.device.values():
                dv.disconnect(isCheck=True, isChange=False,
                              wait=0)  #USE: True -> True
            self.searcher.stopSearch(resetWait=False)
        self.node.stop()

    def connectAntSensor(self, antName, antID, antType, connectStatus):
        if not _SENSOR_ANT:
            return
        self.config.G_ANT['ID'][antName] = antID
        self.config.G_ANT['TYPE'][antName] = antType
        self.config.G_ANT['ID_TYPE'][antName] = struct.pack(
            '<HB', antID, antType)
        antIDType = self.config.G_ANT['ID_TYPE'][antName]
        self.searcher.stopSearch(resetWait=False)

        self.config.G_ANT['USE'][antName] = True

        #existing connection
        if connectStatus:
            self.node.ant.set_wait_period()
            return

        #recconect
        if antIDType in self.device:
            self.device[antIDType].connect(isCheck=False,
                                           isChange=False)  #USE: True -> True)
            self.node.ant.set_wait_period()
            return

        #newly connect
        self.values[antIDType] = {}
        if antType is 0x78:
            self.device[antIDType] \
              = ANT_Device_HeartRate(self.node, self.config, self.values[antIDType], antName)
        elif antType is 0x79:
            self.device[antIDType] \
              = ANT_Device_Speed_Cadence(self.node, self.config ,self.values[antIDType], antName)
        elif antType is 0x7A:
            self.device[antIDType] \
              = ANT_Device_Cadence(self.node, self.config ,self.values[antIDType], antName)
        elif antType is 0x7B:
            self.device[antIDType] \
              = ANT_Device_Speed(self.node, self.config ,self.values[antIDType], antName)
        elif antType is 0x0B:
            self.device[antIDType] \
              = ANT_Device_Power(self.node, self.config ,self.values[antIDType], antName)
        self.node.ant.set_wait_period()

    def disconnectAntSensor(self, antName):
        antIDType = self.config.G_ANT['ID_TYPE'][antName]
        antNames = []
        for k, v in self.config.G_ANT['USE'].items():
            if v and k in self.config.G_ANT['ID_TYPE']:
                if self.config.G_ANT['ID_TYPE'][k] == antIDType:
                    antNames.append(k)
        for k in antNames:
            #USE: True -> False
            self.device[self.config.G_ANT['ID_TYPE'][k]].disconnect(
                isCheck=True, isChange=True)
            self.config.G_ANT['ID_TYPE'][k] = 0
            self.config.G_ANT['ID'][k] = 0
            self.config.G_ANT['TYPE'][k] = 0
            self.config.G_ANT['USE'][k] = False

    def continuousScan(self):
        if not _SENSOR_ANT:
            return
        self.node.ant.set_wait_action()
        for dv in self.device.values():
            dv.disconnect(isCheck=True, isChange=False,
                          wait=0.5)  #USE: True -> True
        time.sleep(0.5)
        self.scanner.scan()
        self.node.ant.set_wait_scan()

    def stopContinuousScan(self):
        self.node.ant.set_wait_action()
        self.scanner.stopScan()
        antIDTypes = []
        for k, v in self.config.G_ANT['USE'].items():
            if v and not self.config.G_ANT['ID_TYPE'][k] in antIDTypes:
                antIDTypes.append(self.config.G_ANT['ID_TYPE'][k])
        for antIDType in antIDTypes:
            self.device[antIDType].connect(isCheck=True,
                                           isChange=False)  #USE: True -> True
        self.node.ant.set_wait_period()
예제 #17
0
파일: manager.py 프로젝트: pirower/test1
class Application:
    _serial_number = 1337
    _frequency = 19  # 0 to 124, x - 2400 (in MHz)

    def __init__(self):

        self._queue = queue.Queue()
        self._beacons = queue.Queue()

        self._node = Node()

        try:
            NETWORK_KEY = [0xA8, 0xA4, 0x23, 0xB9, 0xF5, 0x5E, 0x63, 0xC1]
            self._node.set_network_key(0x00, NETWORK_KEY)

            print("Request basic information...")

            m = self._node.request_message(Message.ID.RESPONSE_CAPABILITIES)
            print("  Capabilities: ", m[2])

            # m = self._node.request_message(Message.ID.RESPONSE_ANT_VERSION)
            # print "  ANT version:  ", struct.unpack("<10sx", m[2])[0]

            # m = self._node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER)
            # print "  Serial number:", struct.unpack("<I", m[2])[0]

            print("Starting system...")

            # NETWORK_KEY= [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1]
            # self._node.set_network_key(0x00, NETWORK_KEY)

            print("Key done...")

            self._channel = self._node.new_channel(
                Channel.Type.BIDIRECTIONAL_RECEIVE)
            self._channel.on_broadcast_data = self._on_data
            self._channel.on_burst_data = self._on_data

            self.setup_channel(self._channel)

            self._worker_thread = threading.Thread(target=self._worker,
                                                   name="ant.fs")
            self._worker_thread.start()
        except Exception as e:
            self.stop()
            raise e

    def _worker(self):
        self._node.start()

    def _main(self):
        try:
            _logger.debug("Link level")
            beacon = self._get_beacon()
            if self.on_link(beacon):
                for i in range(0, 5):
                    beacon = self._get_beacon()
                    if (beacon.get_client_device_state() ==
                            Beacon.ClientDeviceState.AUTHENTICATION):
                        _logger.debug("Auth layer")
                        if self.on_authentication(beacon):
                            _logger.debug("Authenticated")
                            beacon = self._get_beacon()
                            self.on_transport(beacon)
                        self.disconnect()
                        break
        finally:
            _logger.debug("Run 5")
            self.stop()

    def _on_beacon(self, data):
        b = Beacon.parse(data)
        self._beacons.put(b)

    def _on_command(self, data):
        c = ant.fs.command.parse(data)
        self._queue.put(c)

    def _on_data(self, data):
        # print "_on_data", data, len(data)
        if data[0] == 0x43:
            self._on_beacon(data[:8])
            if len(data[8:]) > 0:
                self._on_command(data[8:])
        elif data[0] == 0x44:
            self._on_command(data)

    def _get_beacon(self):
        b = self._beacons.get()
        self._beacons.task_done()
        return b

    def _get_command(self, timeout=15.0):
        _logger.debug("Get command, t%d, s%d", timeout, self._queue.qsize())
        c = self._queue.get(True, timeout)
        self._queue.task_done()
        return c

    def _send_command(self, c):
        data = c.get()
        if len(data) == 8:
            self._channel.send_acknowledged_data(data)
        else:
            self._channel.send_burst_transfer(data)

    # Application actions are defined from here
    # =======================================================================

    # These should be overloaded:

    def setup_channel(self, channel):
        pass

    def on_link(self, beacon):
        pass

    def on_authentication(self, beacon):
        pass

    def on_transport(self, beacon):
        pass

    # Shouldn't have to touch these:

    def start(self):
        self._main()

    def stop(self):
        self._node.stop()

    def _send_commandpipe(self, data):
        # print "send commandpipe", data
        self.upload(0xFFFE, data)

    def _get_commandpipe(self):
        # print "get commandpipe"
        return ant.fs.commandpipe.parse(self.download(0xFFFE))

    def create(self, typ, data, callback=None):
        # print "create", typ
        request = CreateFile(len(data), 0x80, [typ, 0x00, 0x00],
                             [0x00, 0xFF, 0xFF])
        self._send_commandpipe(request.get())
        result = self._get_commandpipe()
        # result._debug()

        if result.get_response() != Response.Response.OK:
            raise AntFSCreateFileException("Could not create file",
                                           result.get_response())

        # print "create result", result, result.get_index(), result.get_data_type(), result.get_identifier()
        # d = self.download_directory()

        # Inform the application that the upload request was successfully created
        if callback is not None:
            callback(0)

        self.upload(result.get_index(), data, callback)
        return result.get_index()

    def upload(self, index, data, callback=None):
        # print "upload", index, len(data)

        iteration = 0
        while True:

            # Request Upload

            # Continue using Last Data Offset (special MAX_ULONG value)
            request_offset = 0 if iteration == 0 else 0xFFFFFFFF
            self._send_command(UploadRequest(index, len(data), request_offset))

            upload_response = self._get_command()
            # upload_response._debug()

            if upload_response._get_argument(
                    "response") != UploadResponse.Response.OK:
                raise AntFSUploadException(
                    "Upload request failed",
                    upload_response._get_argument("response"))

            # Upload data
            offset = upload_response._get_argument("last_data_offset")
            max_block = upload_response._get_argument("maximum_block_size")
            # print " uploading", offset, "to", offset + max_block
            data_packet = data[offset:offset + max_block]
            crc_seed = upload_response._get_argument("crc")
            crc_val = crc(data_packet, upload_response._get_argument("crc"))

            # Pad with 0 to even 8 bytes
            missing_bytes = 8 - (len(data_packet) % 8)
            if missing_bytes != 8:
                data_packet.extend(array.array("B", [0] * missing_bytes))
                # print " adding", str(missing_bytes), "padding"

            # print " packet", len(data_packet)
            # print " crc   ", crc_val, "from seed", crc_seed

            self._send_command(
                UploadDataCommand(crc_seed, offset, data_packet, crc_val))
            upload_data_response = self._get_command()
            # upload_data_response._debug()
            if (upload_data_response._get_argument("response") !=
                    UploadDataResponse.Response.OK):
                raise AntFSUploadException(
                    "Upload data failed",
                    upload_data_response._get_argument("response"))

            if callback is not None and len(data) != 0:
                callback((offset + len(data_packet)) / len(data))

            if offset + len(data_packet) >= len(data):
                # print " done"
                break

            # print " one more"
            iteration += 1

    def download(self, index, callback=None):
        offset = 0
        initial = True
        crc = 0
        data = array.array("B")
        while True:
            _logger.debug("Download %d, o%d, c%d", index, offset, crc)
            self._send_command(DownloadRequest(index, offset, True, crc))
            _logger.debug("Wait for response...")
            try:
                response = self._get_command()
                if response._get_argument(
                        "response") == DownloadResponse.Response.OK:
                    remaining = response._get_argument("remaining")
                    offset = response._get_argument("offset")
                    total = offset + remaining
                    data[offset:total] = response._get_argument(
                        "data")[:remaining]
                    # print "rem", remaining, "offset", offset, "total", total, "size", response._get_argument("size")
                    # TODO: check CRC

                    if callback is not None and response._get_argument(
                            "size") != 0:
                        callback(total / response._get_argument("size"))
                    if total == response._get_argument("size"):
                        return data
                    crc = response._get_argument("crc")
                    offset = total
                else:
                    raise AntFSDownloadException(
                        "Download request failed: ",
                        response._get_argument("response"))
            except queue.Empty:
                _logger.debug("Download %d timeout", index)
                # print "recover from download failure"

    def download_directory(self, callback=None):
        data = self.download(0, callback)
        return Directory.parse(data)

    def set_time(self, time=datetime.datetime.utcnow()):
        """
        :param time: datetime in UTC, or None to set to current time
        """
        utc_tai_diff_seconds = 35
        offset = time - datetime.datetime(1989, 12, 31, 0, 0, 0)
        t = Time(
            int(offset.total_seconds()) + utc_tai_diff_seconds, 0xFFFFFFFF, 0)
        self._send_commandpipe(t.get())

        result = self._get_commandpipe()

        if result.get_response() != TimeResponse.Response.OK:
            raise AntFSTimeException("Failed to set time",
                                     result.get_response())

    def erase(self, index):
        self._send_command(EraseRequestCommand(index))
        response = self._get_command()

        if (response._get_argument("response") !=
                EraseResponse.Response.ERASE_SUCCESSFUL):
            raise AntFSDownloadException("Erase request failed: ",
                                         response._get_argument("response"))

    def link(self):
        self._channel.request_message(Message.ID.RESPONSE_CHANNEL_ID)
        self._send_command(LinkCommand(self._frequency, 4,
                                       self._serial_number))

        # New period, search timeout
        self._channel.set_period(4096)
        self._channel.set_search_timeout(10)
        self._channel.set_rf_freq(self._frequency)

    def authentication_serial(self):
        self._send_command(
            AuthenticateCommand(AuthenticateCommand.Request.SERIAL,
                                self._serial_number))
        response = self._get_command()
        return (response.get_serial(), response.get_data_string())

    def authentication_passkey(self, passkey):
        self._send_command(
            AuthenticateCommand(
                AuthenticateCommand.Request.PASSKEY_EXCHANGE,
                self._serial_number,
                passkey,
            ))

        response = self._get_command()
        if response._get_argument(
                "type") == AuthenticateResponse.Response.ACCEPT:
            return response.get_data_array()
        else:
            raise AntFSAuthenticationException("Passkey authentication failed",
                                               response._get_argument("type"))

    def authentication_pair(self, friendly_name):
        data = array.array("B", map(ord, list(friendly_name)))
        self._send_command(
            AuthenticateCommand(AuthenticateCommand.Request.PAIRING,
                                self._serial_number, data))

        response = self._get_command(30)
        if response._get_argument(
                "type") == AuthenticateResponse.Response.ACCEPT:
            return response.get_data_array()
        else:
            raise AntFSAuthenticationException("Pair authentication failed",
                                               response._get_argument("type"))

    def disconnect(self):
        d = DisconnectCommand(DisconnectCommand.Type.RETURN_LINK, 0, 0)
        self._send_command(d)
예제 #18
0
class AntPlusNode:
    def __init__(self, network_key):
        self.node = Node()
        self.node.set_network_key(0x00, network_key)

    def attach_hrm(self, device_number=0, transfer_type=0):
        channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        hrm = AntPlusHRM(channel,
                         device_number=device_number,
                         transfer_type=transfer_type)
        channel.open()
        return hrm

    def attach_power_meter(self, device_number=0, transfer_type=0):
        channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        pwr_meter = AntPlusPowerMeter(channel,
                                      device_number=device_number,
                                      transfer_type=transfer_type)
        channel.open()
        return pwr_meter

    def attach_speed_sensor(self,
                            wheel_circumference_meters,
                            device_number=0,
                            transfer_type=0):
        channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        sensor = AntPlusSpeedSensor(
            channel,
            wheel_circumference_meters=wheel_circumference_meters,
            device_number=device_number,
            transfer_type=transfer_type)
        channel.open()
        return sensor

    def attach_cadence_sensor(self, device_number=0, transfer_type=0):
        channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        sensor = AntPlusCadenceSensor(channel,
                                      device_number=device_number,
                                      transfer_type=transfer_type)
        channel.open()
        return sensor

    def attach_speed_and_cadence_sensor(self,
                                        wheel_circumference_meters,
                                        device_number=0,
                                        transfer_type=0):
        channel = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        sensor = AntPlusSpeedAndCadenceSensor(
            channel,
            wheel_circumference_meters=wheel_circumference_meters,
            device_number=device_number,
            transfer_type=transfer_type)
        channel.open()
        return sensor

    def attach_combined_speed_and_cadence_sensor(self,
                                                 wheel_circumference_meters,
                                                 speed_device_number=0,
                                                 speed_transfer_type=0,
                                                 cadence_device_number=0,
                                                 cadence_transfer_type=0):
        channel1 = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        channel2 = self.node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        sensor = AntPlusCombinedSpeedAndCadenceSensors(
            channel1,
            channel2,
            wheel_circumference_meters=wheel_circumference_meters,
            speed_device_number=speed_device_number,
            speed_transfer_type=speed_transfer_type,
            cadence_device_number=cadence_device_number,
            cadence_transfer_type=cadence_transfer_type)
        channel1.open()
        channel2.open()
        return sensor

    def start(self):
        self.node.start()

    def stop(self):
        self.node.stop()
예제 #19
0
class AntRower:
    """Ant+ FE Rower signal broadcaster class

    Start an Ant+ FE channel to broadcast the status of the source "Rower".

    """
    def __init__(self, source: Rower, config: dict):
        # only take one parameter, source.
        # source is a object represents a rower, best to be an instance of Rower,
        # it should have a method of get_current_frame(), which returns a K-V dict, containing the rower info.
        # this method is called whenever the current rower data is needed, mostly, in a TX event to send out data.
        self.source = source

        # channel configurations
        self.channel_type = Channel.Type.BIDIRECTIONAL_TRANSMIT  # Master TX
        self.network_key = [0xb9, 0xa5, 0x21, 0xfb, 0xbd, 0x72, 0xc3,
                            0x45]  # Ant+ key
        self.RF_frequency = 57  # Ant+ frequency.
        self.transmission_type = 5  # MSN = 0x0, LSN = 0x5, detail see ant+ device profile document.
        self.device_type = 17  # Ant+ FE
        # if ant device ID not specified, use 1 as default.
        self.device_number = config.get(
            'ANT_DEVICE_ID', 1
        )  # Device number could be whatever between 1~65535. Change if you need.
        self.channel_period = 8192  # 4hz, as device profile requires.

        # openant radio objects.
        self.node = None  # later, when opened, node will be a instance of Node.
        self.channel = None  # later, when opened, will be the assigned channel object on the node.

        # outbound message count
        # used to implement specific transmission pattern, will be rollovered according to the specific pattern.
        # transmission pattern is implemented in function _get_next_page(), for detail check there.
        self.message_count = 1
        # if transmission type does not specifed, use type b as default.
        self.transmission_pattern = config.get(
            'TRANSMISSION_TYPE', 'b'
        )  # 1=a, 2=b, 3=c, 4=d, see Ant+ FE device profile page 24 of 74.
        self.transmission_pattern_func_dict = {
            'a': self._get_next_page_transmission_pattern_a,
            'b': self._get_next_page_transmission_pattern_b,
            'c': self._get_next_page_transmission_pattern_c,
            'd': self._get_next_page_transmission_pattern_d
        }
        self.transmission_pattern_d_internal_count = 1

        # singleton, fixed data pages, 80 and 81
        self.page_80 = DataPage80()
        self.page_81 = DataPage81()

        # thread handler
        self.ant_thread = None

    def on_tx_event(self, data):
        """Callback function for Ant+ (openant)

        Upon each TX time slot, a TX event is generated, and this function is called.
        When called, it should update the rower's data,

        """
        # data = array.array('B', [1, 255, 133, 128, 8, 0, 128, 0])
        page_to_send = self._get_next_page()
        data_payload = page_to_send.to_payload(
        )  # get new data payload to sent at this TX event.
        # call channel's send_broadcast_data to set the TX buffer to new data.
        self.channel.send_broadcast_data(data_payload)
        print("send TX")

    def _open_and_start(self):
        """Open ant+ channel, if no error, start broadcast immediately"""

        # todo: add the try, catch, maybe Node not available, or network key error, or channel can't acquire

        # initialize the ant device, a node represent an ant USB device.
        self.node = Node()
        # set network key at net#0, only net#0 is used.
        self.node.set_network_key(0x00, self.network_key)

        # try get a new TX channel
        self.channel = self.node.new_channel(
            Channel.Type.BIDIRECTIONAL_TRANSMIT)
        # set the callback function for TX tick, each TX tick, this function will be called.
        self.channel.on_TX_event = self.on_tx_event

        # set the channel configurations
        self.channel.set_period(self.channel_period)
        self.channel.set_rf_freq(self.RF_frequency)
        # channel id is defined as <device num, device type, transmission type>
        self.channel.set_id(self.device_number, self.device_type,
                            self.transmission_type)

        # try open channel,
        # once opened, the channel could be found by other devices, but No data sending yet.
        try:
            self.channel.open()
            # start the message loop on the ant device.
            # once started, the messages will be dispatched to callback functions of each channel.
            self.node.start()
        finally:
            self.node.stop()

    def start(self):
        """Start the broadcast event loop, from now on, each TX tick, send new broadcast"""
        self.ant_thread = threading.Thread(target=self._open_and_start)
        self.ant_thread.start()
        # this ensures the function is returned immediately to main thread, not blocking other lines in caller.

    def close(self):
        # todo: cleaning things up.
        pass

    # Transmission pattern handling

    def _get_next_page(self) -> BaseDataPage:
        """Implemented the suggested transmission patterns illustrated in Ant+ FE device profile.

        This function will return the next new page which should be sent out,
        it handles when and what here, according the message count and the specific pattern.

        """
        assert 0 < self.message_count < 133

        if self.message_count == 65 or self.message_count == 66:
            # send page 80
            next_page = self.page_80
        elif self.message_count == 131 or self.message_count == 132:
            # send page 81
            next_page = self.page_81
        else:
            # send data page according to transmission type
            # use dict in a way of switch statement, call different functions of different transmission type.
            next_page = self.transmission_pattern_func_dict[
                self.transmission_pattern]()

        # roll-over to 1 if msg count reaches 132
        if self.message_count == 132:
            self.message_count = 1
            assert self.message_count <= 132
        else:
            self.message_count += 1

        return next_page

    def _get_next_page_transmission_pattern_a(self):
        """Transmission pattern A

        Only send page 16, the minimum requirement.

        :return: data payload of page 16.
        """
        # construct a data page 16 from current rower data of source Rower object.
        return DataPage16(self.source.get_current_frame())

    def _get_next_page_transmission_pattern_b(self):
        """Transmission pattern B

        [16 16 X X ...(64)...] (65)80 (66)80 [(67)16 16 X X ....(130)] (131)81 (132)81

        Mod left part directly, mode right part with minus 2.

        """
        if self.message_count <= 64:
            # left half
            # 1-64, directly mod 4 will be fine, remainder is the sequence.
            # 1 % 4 = 1, 2%4 = 2, 3%4 = 3, 4%4 =0, 5%4 = 1, ..., 8%4 = 0.
            if self.message_count % 4 == 1 or self.message_count % 4 == 2:
                return DataPage16(self.source.get_current_frame())
            else:
                # could only be 3, 0
                return DataPage22(self.source.get_current_frame())
        elif self.message_count >= 67:
            # right half, -2 before mod.
            if ((self.message_count - 2) % 4
                    == 1) or ((self.message_count - 2) % 4 == 2):
                return DataPage16(self.source.get_current_frame())
            else:
                # could only be 3, 0
                return DataPage22(self.source.get_current_frame())

    def _get_next_page_transmission_pattern_c(self):
        """Transmission pattern C

        Note that the illustrated pattern in the document is
        first 64 message are          16 16 X 17 16 16 18 X
        but second 64 messages are    16 16 17 X 16 16 X 18

        Don't know why, but better follow.

        simply look into message count, see which patter should used.

        mod by 8 (right part -2 first), then know which page to go.

        """

        if self.message_count <= 64:
            # left half
            # 1-64, directly mod 8 will be fine, remainder is the sequence.
            msg_count_mod_8 = self.message_count % 8
            assert 0 <= msg_count_mod_8 < 8

            if msg_count_mod_8 == 1 or msg_count_mod_8 == 2:
                return DataPage16(self.source.get_current_frame())
            elif msg_count_mod_8 == 3:
                return DataPage22(self.source.get_current_frame())
            elif msg_count_mod_8 == 4:
                return DataPage17(self.source.get_current_frame())
            elif msg_count_mod_8 == 5 or msg_count_mod_8 == 6:
                return DataPage16(self.source.get_current_frame())
            elif msg_count_mod_8 == 7:
                return DataPage18(self.source.get_current_frame())
            else:
                # could only be 0, so the 8th
                return DataPage22(self.source.get_current_frame())
        elif self.message_count >= 67:
            # right half, -2 before mod.
            msg_count_mod_8 = (self.message_count - 2) % 8
            assert 0 <= msg_count_mod_8 < 8

            if msg_count_mod_8 == 1 or msg_count_mod_8 == 2:
                return DataPage16(self.source.get_current_frame())
            elif msg_count_mod_8 == 3:
                return DataPage17(self.source.get_current_frame())
            elif msg_count_mod_8 == 4:
                return DataPage22(self.source.get_current_frame())
            elif msg_count_mod_8 == 5 or msg_count_mod_8 == 6:
                return DataPage16(self.source.get_current_frame())
            elif msg_count_mod_8 == 7:
                return DataPage22(self.source.get_current_frame())
            else:
                # could only be 0, so the 8th
                return DataPage18(self.source.get_current_frame())

    def _get_next_page_transmission_pattern_d(self):
        """Transmission pattern D

        This pattern is a little bit different from others, pattern repeat in 20 messages, ignore others.

        16 X X X X 16 X X X 18 16 X X X X 16 X X X 17

        1,6,11,16 is page 16
        10, is page 18
        20, page 17
        others, page 22

        this pattern it self repeats, whenever it in time slot for 64 pages of data, after 80 and 81 interrupts,
        it resumes, it's independent from the 64-2-64-2 134 messages pattern.

        So we use a function property to maintain its own pattern counts.
        This property roll-over every 20 data messages.

        """

        assert 0 < self.transmission_pattern_d_internal_count <= 20

        if (self.transmission_pattern_d_internal_count == 1
                or self.transmission_pattern_d_internal_count == 6
                or self.transmission_pattern_d_internal_count == 11
                or self.transmission_pattern_d_internal_count == 16):
            return DataPage16(self.source.get_current_frame())
        elif self.transmission_pattern_d_internal_count == 10:
            return DataPage18(self.source.get_current_frame())
        elif self.transmission_pattern_d_internal_count == 20:
            # roll-over to 1
            self.transmission_pattern_d_internal_count = 1
            # return page 17 for current time
            return DataPage17(self.source.get_current_frame())
        else:
            # should be all page 22.
            return DataPage22(self.source.get_current_frame())
예제 #20
0
class AntSendDemo:
    def __init__(self):

        self.ANTMessageCount = 0
        self.ANTMessagePayload = array.array("B", [0, 0, 0, 0, 0, 0, 0, 0])

        # Init Variables, needed
        self.LastStrideTime = 0
        self.StridesDone = 0
        self.DistanceAccu = 0
        self.Speed_Last = 0
        self.TimeRollover = 0

        self.TimeProgramStart = time.time()
        self.LastTimeEvent = time.time()

        # Building up the Datapages
        # This is just for demo purpose and can/will look diverent for every implementation

    def Create_Next_DataPage(self):
        # Define Variables
        UpdateLatency_7 = 0

        self.ANTMessageCount += 1

        # Time Calculations
        self.ElapsedSseconds = time.time() - self.LastTimeEvent
        self.LastTimeEvent = time.time()
        UpdateLatency_7 += self.ElapsedSseconds  # 1Second / 32 = 0,03125
        UL_7 = int(UpdateLatency_7 / 0.03125)

        # Stride Count, Accumulated strides.
        # This value is incremented once for every two footfalls.
        StrideCountUpValue = 60.0 / (TreadmillCadence / 2.0
                                     )  # In our Example 0,75
        while self.LastStrideTime > StrideCountUpValue:
            self.StridesDone += 1
            self.LastStrideTime -= StrideCountUpValue
        self.LastStrideTime += self.ElapsedSseconds
        if self.StridesDone > 255:
            self.StridesDone -= 255

        # DISTANCE
        # Accumulated distance, in m-Meters, Rollover = 256
        self.DistanceBetween = self.ElapsedSseconds * TreadmillSpeed
        self.DistanceAccu += (
            self.DistanceBetween
        )  # Add Distance beetween 2 ANT+ Ticks to Accumulated Distance
        if self.DistanceAccu > 255:
            self.DistanceAccu -= 255

        self.distance_H = int(self.DistanceAccu)  # just round it to INT
        self.DistanceLow_HEX = int((self.DistanceAccu - self.distance_H) * 16)

        # SPEED - Berechnung
        self.var_speed_ms_H = int(TreadmillSpeed)  # INT-Value
        self.var_speed_ms_L = int(
            TreadmillSpeed * 1000) - (self.var_speed_ms_H * 1000)
        self.var_speed_ms_L_HEX = int(
            (TreadmillSpeed - self.var_speed_ms_H) * 256)

        # TIME (chnages to Distance or speed will effect if This byte needs to be calculated (<= check Specifikation)
        if self.Speed_Last != TreadmillSpeed or self.Distance_Last != self.DistanceAccu:
            self.TimeRollover += self.ElapsedSseconds
            if self.TimeRollover > 255:
                self.TimeRollover -= 255

        self.TimeRollover_H = int(self.TimeRollover)
        # only integer
        if self.TimeRollover_H > 255:
            self.TimeRollover_H = 255
        self.TimeRollover_L_HEX = int(
            (self.TimeRollover - self.TimeRollover_H) * 200)
        if self.TimeRollover_L_HEX > 255:
            self.TimeRollover_L_HEX -= 255
        self.Speed_Last = TreadmillSpeed
        self.Distance_Last = self.DistanceAccu

        if self.ANTMessageCount < 3:
            self.ANTMessagePayload[0] = 80  # DataPage 80
            self.ANTMessagePayload[1] = 0xFF
            self.ANTMessagePayload[2] = 0xFF  # Reserved
            self.ANTMessagePayload[3] = 1  # HW Revision
            self.ANTMessagePayload[4] = 1
            self.ANTMessagePayload[5] = 1  # Manufacturer ID
            self.ANTMessagePayload[6] = 1
            self.ANTMessagePayload[7] = 1  # Model Number

        elif self.ANTMessageCount > 64 and self.ANTMessageCount < 67:
            self.ANTMessagePayload[0] = 81  # DataPage 81
            self.ANTMessagePayload[1] = 0xFF
            self.ANTMessagePayload[2] = 0xFF  # Reserved
            self.ANTMessagePayload[3] = 1  # SW Revision
            self.ANTMessagePayload[4] = 0xFF
            self.ANTMessagePayload[5] = 0xFF  # Serial Number
            self.ANTMessagePayload[6] = 0xFF
            self.ANTMessagePayload[7] = 0xFF  # Serial Number

        else:
            self.ANTMessagePayload[0] = 0x01  # Data Page 1
            self.ANTMessagePayload[1] = self.TimeRollover_L_HEX
            self.ANTMessagePayload[2] = self.TimeRollover_H  # Reserved
            self.ANTMessagePayload[
                3] = self.distance_H  # Distance Accumulated INTEGER
            # BYTE 4 - Speed-Integer & Distance-Fractional
            self.ANTMessagePayload[4] = (self.DistanceLow_HEX * 16 +
                                         self.var_speed_ms_H
                                         )  # Instaneus Speed, Note: INTEGER
            self.ANTMessagePayload[
                5] = self.var_speed_ms_L_HEX  # Instaneus Speed, Fractional
            self.ANTMessagePayload[6] = self.StridesDone  # Stride Count
            self.ANTMessagePayload[7] = UL_7  # Update Latency

            # ANTMessageCount reset
            if self.ANTMessageCount > 131:
                self.ANTMessageCount = 0

        return self.ANTMessagePayload

    # TX Event
    def on_event_tx(self, data):
        ANTMessagePayload = self.Create_Next_DataPage()
        self.ActualTime = time.time() - self.TimeProgramStart

        # ANTMessagePayload = array.array('B', [1, 255, 133, 128, 8, 0, 128, 0])    # just for Debuggung pourpose

        self.channel.send_broadcast_data(
            self.ANTMessagePayload)  # Final call for broadcasting data
        print(
            self.ActualTime,
            "TX:",
            Device_Number,
            ",",
            Device_Type,
            ":",
            format_list(ANTMessagePayload),
        )

    # Open Channel
    def OpenChannel(self):

        self.node = Node()  # initialize the ANT+ device as node

        # CHANNEL CONFIGURATION
        self.node.set_network_key(0x00, NETWORK_KEY)  # set network key
        self.channel = self.node.new_channel(
            Channel.Type.BIDIRECTIONAL_TRANSMIT, 0x00,
            0x00)  # Set Channel, Master TX
        self.channel.set_id(
            Device_Number, Device_Type, 5
        )  # set channel id as <Device Number, Device Type, Transmission Type>
        self.channel.set_period(Channel_Period)  # set Channel Period
        self.channel.set_rf_freq(Channel_Frequency)  # set Channel Frequency

        # Callback function for each TX event
        self.channel.on_broadcast_tx_data = self.on_event_tx

        try:
            self.channel.open(
            )  # Open the ANT-Channel with given configuration
            self.node.start()
        except KeyboardInterrupt:
            print("Closing ANT+ Channel...")
            self.channel.close()
            self.node.stop()
        finally:
            print("Final checking...")
        channel.set_period(8070)
        channel.set_search_timeout(255)
        channel.set_rf_freq(57)
        channel.set_id(0, 120, 0)

        return channel

    def on_data(self, data):
        self.write_file(data[7])

    def write_file(self, heartrate):
        f = file('/tmp/messages/heartrate', 'w')
        f.write('Heartrate: ' + str(heartrate))
        f.close()


logging.basicConfig()
logging.disable(logging.ERROR)

node = Node()
node.set_network_key(0x00, NETWORK_KEY)
heartrate = HeartRate()

channel_heart = heartrate.setup_channel(node)

try:
    channel_heart.open()
    node.start()
finally:
    node.stop()
예제 #22
0
class AntServer():
  def __init__(self, netkey, ant_devices, websocket_server):
    self.ant_devices = ant_devices
    self.channels = []
    self.netkey = netkey
    self.antnode = None
    self.websocket_server = websocket_server
    self.ant_modes = {
      "speed_cad" : {
        "device_id" : 121,
        "period" : 8086,
        "freq" : 57,
        "ant_name" : "C:CAD",
        "handler" : "_handle_speed_cad"
      },
      "hr" : {
        "device_id" : 120,
        "period" : 8070,
        "freq" : 57,
        "ant_name" : "C:HRM",
        "handler" : "_handle_hr"
      },
      "power" : {
        "device_id" : 11,
        "period" : 8182,
        "freq" : 57,
        "ant_name" : "C:PWR",
        "handler" : "_handle_power"
      }
    }

    self.parsers = {
      "hr" : antparsers.Hr(),
      "power" : antparsers.Power(),
      "speed_cad" : antparsers.SpeedCadence()
    }
    self._log_raw_data = False
    self._log_decoded = True


  @staticmethod
  def setup_and_start(websocket_server, config):
    NETKEY = [0xb9, 0xa5, 0x21, 0xfb, 0xbd, 0x72, 0xc3, 0x45]
    antserver = None
    for i in range(1, 3):
      try:
        antserver = AntServer(NETKEY, config["devices"], websocket_server)
        antserver._log_raw_data = config["raw_logging"]
        antserver._log_decoded = config["verbose"]
        try:
          antserver.start()
        except KeyboardInterrupt:
          pass
        finally:
          if antserver:
            antserver.stop()
        break
      except AntException:
        print("ERR: Failed to setup ant server. Retrying... %s" % i)

  def start(self):
    if not os.path.isdir("logs"):
      os.mkdir("logs")
    self.logfile = datetime.strftime(datetime.now(), "logs/%Y%m%d_%H%M.txt")
    self.logfile_handle = open(self.logfile, "w")

    self._setup_channels()

  def stop(self):
    if self.antnode:
      self.antnode.stop()

  def __enter__(self):
    return self

  def _setup_channels(self):
    try:
      self.antnode = Node()
    except USBError:
      self.antnode = None
      print("""

Fatal error: Could not connect to ANT USB dongle
Please make sure no other application is using it, e.g. Garmin ANT Agent

""")
      return

    self.antnode.set_network_key(0x00, self.netkey)

    for device in self.ant_devices:
      print("Registering device : %s : %s" % (device, self.ant_modes[device]))
      dev = self.ant_modes[device]

      c = self.antnode.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
      c.set_id(0, dev["device_id"], 0)
      c.set_search_timeout(12)
      c.set_period(dev["period"])
      c.set_rf_freq(dev["freq"])
      m = getattr(self, dev["handler"])
      c.on_broadcast_data = m
      c.on_burst_data = m
      c.open()
      self.channels.append(c)

    self.antnode.start()

  def _log_raw(self, event_type, msg):
    if self._log_raw_data:
      data = []
      for m in msg:
        data.append(m)
      m = json.dumps({"raw" : "true",
                      "event_type" : event_type,
                      "time_millis" : int(time.time() * 1000),
                      "data" : data})
      self.logfile_handle.write(m)
      self.logfile_handle.write("\n")
      self.logfile_handle.flush()
      os.fsync(self.logfile_handle.fileno())

  def _parse_and_send(self, event_type, data):
    self._log_raw(event_type, data)

    parser = self.parsers[event_type]
    values = parser.parse(data)
    if values:
      for value in antparsers.Parser.to_json(values):
        self.websocket_server.send_to_all(value)
        if self._log_decoded:
          print(value)

  def _handle_hr(self, data):
    self._parse_and_send("hr", data)

  def _handle_speed_cad(self, data):
    self._parse_and_send("speed_cad", data)

  def _handle_power(self, data):
    self._parse_and_send("power", data)

  def __exit__(self, type_, value, traceback):
    self.stop()
    self.logfile_handle.close()
class Application:
    
    _serial_number = 1337
    _frequency     = 19    # 0 to 124, x - 2400 (in MHz)
    
    def __init__(self):

        self._queue = Queue.Queue()
        self._beacons = Queue.Queue()

        self._node = Node(0x0fcf, 0x1008)

        print "Request basic information..."
        m = self._node.request_message(Message.ID.RESPONSE_VERSION)
        print "  ANT version:  ", struct.unpack("<10sx", m[2])[0]
        m = self._node.request_message(Message.ID.RESPONSE_CAPABILITIES)
        print "  Capabilities: ", m[2]
        m = self._node.request_message(Message.ID.RESPONSE_SERIAL_NUMBER)
        print "  Serial number:", struct.unpack("<I", m[2])[0]

        print "Starting system..."

        NETWORK_KEY= [0xa8, 0xa4, 0x23, 0xb9, 0xf5, 0x5e, 0x63, 0xc1]

        self._node.reset_system()
        self._node.set_network_key(0x00, NETWORK_KEY)

        self._channel = self._node.new_channel(Channel.Type.BIDIRECTIONAL_RECEIVE)
        self._channel.on_broadcast_data = self._on_data
        self._channel.on_burst_data = self._on_data
        
        self.setup_channel(self._channel)
        
        self._worker_thread =threading.Thread(target=self._worker, name="ant.fs")
        self._worker_thread.start()

    def _worker(self):
        self._node.start()

    def _main(self):
        try:
            _logger.debug("Link level")
            beacon = self._get_beacon()
            if self.on_link(beacon):
                for i in range(0, 5):
                    beacon = self._get_beacon()
                    if beacon.get_client_device_state() == Beacon.ClientDeviceState.AUTHENTICATION:
                        _logger.debug("Auth layer")
                        if self.on_authentication(beacon):
                            _logger.debug("Authenticated")
                            beacon = self._get_beacon()
                            self.on_transport(beacon)
                        self.disconnect()
                        break
        except Exception as e:
            print e
            for line in traceback.format_exc().splitlines():
                _logger.error("%r", line)
        finally:
            _logger.debug("Run 5")
            self.stop()


    def _on_beacon(self, data):
        b = Beacon.parse(data)
        self._beacons.put(b)

    def _on_command(self, data):
        c = ant.fs.command.parse(data)
        self._queue.put(c)

    def _on_data(self, data):
        #print "_on_data", data, len(data)
        if data[0] == 0x43:
            self._on_beacon(data[:8])
            if len(data[8:]) > 0:
                self._on_command(data[8:])
        elif data[0] == 0x44:
            self._on_command(data)
    
    def _get_beacon(self):
        b = self._beacons.get()
        self._beacons.task_done()
        return b
    
    def _get_command(self, timeout=3.0):
        _logger.debug("Get command, t%d, s%d", timeout, self._queue.qsize())
        c = self._queue.get(True, timeout)
        self._queue.task_done()
        return c
    
    def _send_command(self, c):
        data = c.get()
        if len(data) == 8:
            self._channel.send_acknowledged_data(data)
        else:
            self._channel.send_burst_transfer(data)
    
    # Application actions are defined from here
    # =======================================================================
    
    # These should be overloaded:
    
    def setup_channel(self, channel):
        pass
    
    def on_link(self, beacon):
        pass
    
    def on_authentication(self, beacon):
        pass
    
    def on_transport(self, beacon):
        pass
    
    # Shouldn't have to touch these:
    
    def start(self):
        self._main()

    def stop(self):
        self._node.stop()
    
    def erase(self, index):
        pass
    
    def upload(self, index, data):
        pass
    
    def download(self, index, callback=None):
        offset  = 0
        initial = True
        crc     = 0
        data    = array.array('B')
        while True:
            _logger.debug("Download %d, o%d, c%d", index, offset, crc)
            self._send_command(DownloadRequest(index, offset, True, crc))
            _logger.debug("Wait for response...")
            try:
                response = self._get_command()
                if response._get_argument("response") == DownloadResponse.Response.OK:
                    remaining    = response._get_argument("remaining")
                    offset       = response._get_argument("offset")
                    total        = offset + remaining
                    data[offset:total] = response._get_argument("data")[:remaining]
                    #print "rem", remaining, "offset", offset, "total", total, "size", response._get_argument("size")
                    if callback != None:
                        callback(float(total) / float(response._get_argument("size")))
                    if total == response._get_argument("size"):
                        return data
                    crc = response._get_argument("crc")
                    offset = total
                else:
                    raise AntFSDownloadException(response._get_argument("response"))
            except Queue.Empty:
                _logger.debug("Download %d timeout", index)
                #print "recover from download failure"
    
    def download_directory(self, callback=None):
        data = self.download(0, callback)
        return Directory.parse(data)

    def link(self):
        self._channel.request_message(Message.ID.RESPONSE_CHANNEL_ID)
        self._send_command(LinkCommand(self._frequency, 4, self._serial_number))
       
        # New period, search timeout
        self._channel.set_period(4096)
        self._channel.set_search_timeout(3)
        self._channel.set_rf_freq(self._frequency)

    def authentication_serial(self):
        self._send_command(AuthenticateCommand(
                AuthenticateCommand.Request.SERIAL,
                self._serial_number))
        response = self._get_command()
        return (response.get_serial(), response.get_data_string())

    def authentication_passkey(self, passkey):
        self._send_command(AuthenticateCommand(
                AuthenticateCommand.Request.PASSKEY_EXCHANGE,
                self._serial_number, passkey))

        response = self._get_command()
        if response._get_argument("type") == AuthenticateResponse.Response.ACCEPT:
            return response.get_data_array()
        else:
            raise AntFSAuthenticationException(response._get_argument("type"))

    def authentication_pair(self, friendly_name):
        data = array.array('B', map(ord, list(friendly_name)))
        self._send_command(AuthenticateCommand(
                AuthenticateCommand.Request.PAIRING,
                self._serial_number, data))

        response = self._get_command(30)
        if response._get_argument("type") == AuthenticateResponse.Response.ACCEPT:
            return response.get_data_array()
        else:
            raise AntFSAuthenticationException(response._get_argument("type"))
        

    def disconnect(self):
        d = DisconnectCommand(DisconnectCommand.Type.RETURN_LINK, 0, 0)
        self._send_command(d)