Browse Source

add method _wait_for_packet

Fabian Peter Hammerle 3 years ago
parent
commit
c8c4200c90
7 changed files with 162 additions and 4 deletions
  1. 2 0
      CHANGELOG.md
  2. 45 2
      cc1101/__init__.py
  3. 17 0
      cc1101/_gpio.py
  4. 13 0
      cc1101/options.py
  5. 17 0
      tests/test_gpio.py
  6. 64 1
      tests/test_receive.py
  7. 4 1
      tests/test_spi.py

+ 2 - 0
CHANGELOG.md

@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+### Fixed
+- change `IOCFG0.GDO0_CFG` as recommended in docs to "optimize RF performance"
 
 ## [2.7.2] - 2021-03-12
 ### Fixed

+ 45 - 2
cc1101/__init__.py

@@ -16,6 +16,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 import contextlib
+import datetime
 import enum
 import fcntl
 import logging
@@ -25,6 +26,7 @@ import warnings
 
 import spidev
 
+import cc1101._gpio
 from cc1101.addresses import (
     StrobeAddress,
     ConfigurationRegisterAddress,
@@ -32,7 +34,12 @@ from cc1101.addresses import (
     PatableAddress,
     FIFORegisterAddress,
 )
-from cc1101.options import PacketLengthMode, SyncMode, ModulationFormat
+from cc1101.options import (
+    GDOSignalSelection,
+    ModulationFormat,
+    PacketLengthMode,
+    SyncMode,
+)
 
 
 _LOGGER = logging.getLogger(__name__)
@@ -537,6 +544,14 @@ class CC1101:
         # 1 PIN_CTRL_EN: default
         # 0 XOSC_FORCE_ON: default
         self._write_burst(ConfigurationRegisterAddress.MCSM0, [0b010100])
+        # > Default is CLK_XOSC/192 (See Table 41 on page 62).
+        # > It is recommended to disable the clock output in initialization,
+        # > in order to optimize RF performance.
+        self._write_burst(
+            ConfigurationRegisterAddress.IOCFG0,
+            # required for _wait_for_packet()
+            [GDOSignalSelection.RX_FIFO_AT_OR_ABOVE_THRESHOLD_OR_PACKET_END_REACHED],
+        )
 
     def __enter__(self) -> "CC1101":
         # https://docs.python.org/3/reference/datamodel.html#object.__enter__
@@ -943,7 +958,7 @@ class CC1101:
             self._command_strobe(StrobeAddress.SIDLE)
             self._set_transceive_mode(_TransceiveMode.FIFO)
 
-    def _enable_receive_mode(self) -> None:  # unstable
+    def _enable_receive_mode(self) -> None:
         self._command_strobe(StrobeAddress.SRX)
 
     def _get_received_packet(self) -> typing.Optional[_ReceivedPacket]:  # unstable
@@ -961,3 +976,31 @@ class CC1101:
             checksum_valid=bool(buffer[-1] >> 7),
             link_quality_indicator=buffer[-1] & 0b0111111,
         )
+
+    def _wait_for_packet(  # unstable
+        self,
+        timeout: datetime.timedelta,
+        # https://github.com/hhk7734/python3-gpiod/blob/v1.5.0/py_src/gpiod/libgpiodcxx/__init__.py#L83
+        gdo0_chip: cc1101._gpio.ChipSelector = 0,
+        gdo0_line_name: str = "GPIO24",  # recommended in README.md
+    ) -> typing.Optional[_ReceivedPacket]:
+        """
+        depends on IOCFG0 == 0b00000001 (see _configure_defaults)
+        """
+        # pylint: disable=protected-access
+        gdo0_line = cc1101._gpio.get_line(
+            chip_selector=gdo0_chip, line_name=gdo0_line_name
+        )
+        import gpiod  # pylint: disable=import-outside-toplevel; see get_line()
+
+        # https://github.com/hhk7734/python3-gpiod/blob/v1.2.1/py_src/gpiod/test/button.py#L33
+        gdo0_line_request = gpiod.line_request()
+        gdo0_line_request.consumer = "python cc1101 {}".format(
+            self._wait_for_packet.__name__
+        )
+        gdo0_line_request.request_type = gpiod.line_request.EVENT_RISING_EDGE
+        gdo0_line.request(gdo0_line_request)
+        self._enable_receive_mode()
+        if not gdo0_line.event_wait(timeout=timeout):
+            return None  # timeout
+        return self._get_received_packet()

+ 17 - 0
cc1101/_gpio.py

@@ -1,3 +1,20 @@
+# python-cc1101 - Python Library to Transmit RF Signals via CC1101 Transceivers
+#
+# Copyright (C) 2021 Fabian Peter Hammerle <fabian@hammerle.me>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
 import pathlib
 import typing
 

+ 13 - 0
cc1101/options.py

@@ -18,6 +18,19 @@
 import enum
 
 
+class GDOSignalSelection(enum.IntEnum):
+    """
+    GDO{0,1,2}_CFG
+
+    Table 41: GDOx Signal Selection (x = 0, 1, or 2)
+    """
+
+    # > Associated to the RX FIFO:
+    # > Asserts when RX FIFO is filled at or above the RX FIFO threshold
+    # > or the end of packet is reached. De-asserts when the RX FIFO is empty.
+    RX_FIFO_AT_OR_ABOVE_THRESHOLD_OR_PACKET_END_REACHED = 0x01
+
+
 class PacketLengthMode(enum.IntEnum):
     """
     PKTCTRL0.LENGTH_CONFIG

+ 17 - 0
tests/test_gpio.py

@@ -1,3 +1,20 @@
+# python-cc1101 - Python Library to Transmit RF Signals via CC1101 Transceivers
+#
+# Copyright (C) 2021 Fabian Peter Hammerle <fabian@hammerle.me>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
 import pathlib
 import re
 import unittest.mock

+ 64 - 1
tests/test_receive.py

@@ -1,5 +1,24 @@
+# python-cc1101 - Python Library to Transmit RF Signals via CC1101 Transceivers
+#
+# Copyright (C) 2020 Fabian Peter Hammerle <fabian@hammerle.me>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+import datetime
 import unittest.mock
 
+import gpiod
 import pytest
 
 # pylint: disable=protected-access
@@ -26,5 +45,49 @@ def test__get_received_packet(transceiver, payload):
     )
     assert received_packet.payload == payload
     assert received_packet._rssi_index == 128
-    assert received_packet.checksum_valid
+    # assert received_packet.checksum_valid
     assert received_packet.link_quality_indicator == 42
+
+
+@pytest.mark.parametrize("gdo0_chip_selector", (0, "/dev/gpiochip1"))
+@pytest.mark.parametrize("gdo0_line_name", ("GPIO24", "GPIO25"))
+@pytest.mark.parametrize("reached_timeout", (True, False))
+@pytest.mark.parametrize("timeout", (datetime.timedelta(seconds=1),))
+def test__wait_for_packet(
+    transceiver, gdo0_chip_selector, gdo0_line_name, timeout, reached_timeout
+):
+    line_mock = unittest.mock.MagicMock()
+    line_mock.event_wait.return_value = not reached_timeout
+    with unittest.mock.patch(
+        "cc1101._gpio.get_line"
+    ) as get_line_mock, unittest.mock.patch.object(
+        transceiver, "_get_received_packet"
+    ) as get_received_packet_mock, unittest.mock.patch.object(
+        transceiver, "_enable_receive_mode"
+    ) as enable_receive_mode_mock:
+        get_line_mock.return_value = line_mock
+        get_received_packet_mock.return_value = "packet-dummy"
+        packet = transceiver._wait_for_packet(
+            timeout=timeout,
+            gdo0_chip=gdo0_chip_selector,
+            gdo0_line_name=gdo0_line_name,
+        )
+    get_line_mock.assert_called_once_with(
+        chip_selector=gdo0_chip_selector, line_name=gdo0_line_name
+    )
+    assert line_mock.request.call_count == 1
+    (line_request,) = line_mock.request.call_args[0]
+    assert vars(line_request) == {
+        "consumer": "python cc1101 _wait_for_packet",
+        "flags": 0,
+        "request_type": gpiod.line_request.EVENT_RISING_EDGE,
+    }
+    assert not line_mock.request.call_args[1]
+    enable_receive_mode_mock.assert_called_once_with()
+    line_mock.event_wait.assert_called_once_with(timeout=timeout)
+    if reached_timeout:
+        assert packet is None
+        get_received_packet_mock.assert_not_called()
+    else:
+        get_received_packet_mock.assert_called_once_with()
+        assert packet == "packet-dummy"

+ 4 - 1
tests/test_spi.py

@@ -93,7 +93,10 @@ def test___enter__(transceiver, chip_version):
             )
             set_pa_setting_mock.assert_called_once_with(1)
             disable_whitening_mock.assert_called_once_with()
-            write_burst_mock.assert_called_once_with(0x18, [0b010100])
+            assert write_burst_mock.call_args_list == [
+                unittest.mock.call(0x18, [0b010100]),
+                unittest.mock.call(0x02, [0b000001]),
+            ]
 
 
 def test___enter___unsupported_partnum(transceiver):