diff --git a/README.md b/README.md index 4d1d521..d2c8ad3 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -![PyPacket](https://i.imgur.com/MZYHAFG.png "PyPacket") +

-A simple CLI logger to receive and decode APRS packets via rtl_fm ([RTL-SDR](http://osmocom.org/projects/sdr/wiki/rtl-sdr)) and [multimon-ng](https://github.com/EliasOenal/multimon-ng). This project serves as an open source expirimental tool for research into the RF spectrum and APRS. +A simple CLI tool to receive, decode, log [APRS](http://www.aprs.org/) packets via rtl_fm ([RTL-SDR](http://osmocom.org/projects/sdr/wiki/rtl-sdr)) and [multimon-ng](https://github.com/EliasOenal/multimon-ng). This project serves as an open source expirimental tool for research into the RF spectrum and APRS. [![Build Status](https://travis-ci.org/cceremuga/pypacket.svg?branch=master)](https://travis-ci.org/cceremuga/pypacket) -## Dependencies +## Requirements -Requires the following to be installed and configured on your system in order to run. +The following are required to be installed and configured on your system. * Some form of Linux flavor. MacOS, possibly. Windows, doubtful. * An RTL-SDR compatible device. @@ -18,43 +18,44 @@ Requires the following to be installed and configured on your system in order to ## Configuration -The `config/configuration.json` file contains all of the current configuration options including frequency, gain, etc. More options will be added as needed. +The `config/configuration.json` file contains all of the configuration options including frequency, gain, etc. More options will be added as needed. ## Running -From the directory you've cloned the repository to, simply execute `python main.py`. The application will start and immediately begin listening on the configured frequency. +From the directory you've cloned the repository to, simply run `python main.py` in the shell of your choice. The application will start and immediately begin listening on the configured frequency. Logged packets will be output to your terminal and written to a file in the `logs` directory. ## Recent Patch Notes -* 4/18/17 - * Basic JSON-based configuration support. +* 4/18/2017 (v1.0) + * Improved RTL settings. + * Completed JSON configuration support. * Improved logging. * Resolved bug when logs directory did not exist. -* 4/17/17 +* 4/17/2017 (v0.9) * Logging runtime activities to file in the logs subdirectory. -* 4/2/17 +* 4/2/2017 (v0.8) * Start of unit tests. * Travis CI integration. -## Current / Future Plans +## Feature Roadmap -* JSON configuration options for frequency, gain [in progress]. -* APRS frame deserialization for human readability [future]. -* Quality code coverage [future]. -* Better documentation [future]. -* Performance optimization [future]. -* Simple TCP server (for use in Xastir etc.) [future]. -* Custom IGate uploading [future]. +* APRS frame deserialization for human readability [v2.0]. +* Better documentation [v2.0]. +* Performance optimization [v2.0]. +* Simple TCP server (for use in Xastir etc.) [v3.0]. +* Custom IGate uploading [v4.0]. ## Contributing -You are welcome to contribute by submitting pull requests on GitHub as you see fit! +You are welcome to contribute by submitting pull requests on GitHub if you are interested in development. + +Feature / enhancement requests may be submitted via GitHub issues. ## Credits -Thanks to the following projects / libraries for open source code / inspiration. +Thanks to the following projects / libraries for open source code / inspiration / Creative Commons resources. * [pimultimonaprs](https://github.com/asdil12/pymultimonaprs) * [The Noun Project](https://thenounproject.com/search/?q=radio%20tower&i=749293) diff --git a/config/configuration.json b/config/configuration.json index 580bd4d..857b499 100644 --- a/config/configuration.json +++ b/config/configuration.json @@ -1,6 +1,7 @@ { - "radio": { + "rtl": { "frequency": "144390000", - "gain": "49.6" + "gain": "49.6", + "sample_rate": "22050" } } diff --git a/pypacket/base/configuration.py b/pypacket/base/configuration.py index 6d3202f..68808af 100644 --- a/pypacket/base/configuration.py +++ b/pypacket/base/configuration.py @@ -6,18 +6,16 @@ class Configuration: def __init__(self): self.data = {} self.load() - self.validate() def load(self): with open(self.CONFIG_FILE_NAME) as json_data_file: self.data = json.load(json_data_file) def frequency(self): - return self.data['radio']['frequency'] + return self.data['rtl']['frequency'] def gain(self): - return self.data['radio']['gain'] + return self.data['rtl']['gain'] - def validate(self): - pass - # TODO: Validate configuration. + def sample_rate(self): + return self.data['rtl']['sample_rate'] diff --git a/pypacket/base/listener.py b/pypacket/base/listener.py index 46c0093..6b13cce 100644 --- a/pypacket/base/listener.py +++ b/pypacket/base/listener.py @@ -20,8 +20,8 @@ def start(self): self.log_handler.log_info('Starting rtl_fm subprocess.') rtl_subprocess = subprocess.Popen( - ['rtl_fm', '-f', self.config.frequency(), '-s', '22050', '-o', '4', - '-g', self.config.gain(), '-'], + ['rtl_fm', '-M', 'fm', '-f', self.config.frequency(), '-s', + self.config.sample_rate(), '-l', '0', '-g', self.config.gain(), '-'], stdout=subprocess.PIPE, stderr=open('/dev/null') ) diff --git a/tests/base/__init__.py b/tests/base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/base/test_configuration.py b/tests/base/test_configuration.py new file mode 100644 index 0000000..5edb4ed --- /dev/null +++ b/tests/base/test_configuration.py @@ -0,0 +1,26 @@ +from pypacket.base.configuration import Configuration + +class TestConfiguration: + def test_frequency_expect_frequency(self, capsys): + test_configuration = Configuration() + expected_frequency = '144390000' + + actual_frequency = test_configuration.frequency() + + assert actual_frequency == expected_frequency + + def test_sample_rate_expect_sample_rate(self, capsys): + test_configuration = Configuration() + expected_sample_rate = '22050' + + actual_sample_rate = test_configuration.sample_rate() + + assert actual_sample_rate == expected_sample_rate + + def test_gain_expect_gain(self, capsys): + test_configuration = Configuration() + expected_gain = '49.6' + + actual_gain = test_configuration.gain() + + assert actual_gain == expected_gain diff --git a/tests/util/test_colors.py b/tests/util/test_colors.py index 57be7f5..92a5cb1 100644 --- a/tests/util/test_colors.py +++ b/tests/util/test_colors.py @@ -2,21 +2,21 @@ class TestColors: def test_green_expect_green_value(self): - expectedColor = '\033[92m' - assert Colors.GREEN == expectedColor + expected_color = '\033[92m' + assert Colors.GREEN == expected_color def test_yellow_expect_yellow_value(self): - expectedColor = '\033[93m' - assert Colors.YELLOW == expectedColor + expected_color = '\033[93m' + assert Colors.YELLOW == expected_color def test_red_expect_red_value(self): - expectedColor = '\033[91m' - assert Colors.RED == expectedColor + expected_color = '\033[91m' + assert Colors.RED == expected_color def test_reset_expect_reset_value(self): - expectedColor = '\033[0m' - assert Colors.RESET == expectedColor + expected_color = '\033[0m' + assert Colors.RESET == expected_color def test_blue_expect_blue_value(self): - expectedColor = '\033[94m' - assert Colors.BLUE == expectedColor + expected_color = '\033[94m' + assert Colors.BLUE == expected_color diff --git a/tests/util/test_logger.py b/tests/util/test_logger.py index 46ce3ce..ed5fc0e 100644 --- a/tests/util/test_logger.py +++ b/tests/util/test_logger.py @@ -5,41 +5,41 @@ class TestLogger: PRINT_SUFFIX = '\n' def test_log_info_expect_logged(self, capsys): - mockInfo = 'Hey, check out this info.' - expectedInfo = Colors.BLUE + Logger.SYS_PREFIX + Colors.RESET + \ - mockInfo + self.PRINT_SUFFIX + mock_info = 'Hey, check out this info.' + expected_info = Colors.BLUE + Logger.SYS_PREFIX + Colors.RESET + \ + mock_info + self.PRINT_SUFFIX - Logger().log_info(mockInfo) + Logger().log_info(mock_info) out, err = capsys.readouterr() - assert out == expectedInfo + assert out == expected_info def test_log_error_expect_logged(self, capsys): - mockError = 'Hey, check out this error.' - expectedError = Colors.RED + Logger.ERR_PREFIX + Colors.RESET + \ - mockError + self.PRINT_SUFFIX + mock_error = 'Hey, check out this error.' + expected_error = Colors.RED + Logger.ERR_PREFIX + Colors.RESET + \ + mock_error + self.PRINT_SUFFIX - Logger().log_error(mockError) + Logger().log_error(mock_error) out, err = capsys.readouterr() - assert out == expectedError + assert out == expected_error def test_log_warn_expect_logged(self, capsys): - mockWarn = 'Hey, check out this warning.' - expectedWarn = Colors.YELLOW + Logger.WRN_PREFIX + Colors.RESET + \ - mockWarn + self.PRINT_SUFFIX + mock_warn = 'Hey, check out this warning.' + expected_warn = Colors.YELLOW + Logger.WRN_PREFIX + Colors.RESET + \ + mock_warn + self.PRINT_SUFFIX - Logger().log_warn(mockWarn) + Logger().log_warn(mock_warn) out, err = capsys.readouterr() - assert out == expectedWarn + assert out == expected_warn def test_log_packet_expect_logged(self, capsys): - mockPacket = 'Hey, check out this packet.' - expectedPacket = Colors.GREEN + Logger.REC_PREFIX + Colors.RESET + \ - mockPacket + self.PRINT_SUFFIX + mock_packet = 'Hey, check out this packet.' + expected_packet = Colors.GREEN + Logger.REC_PREFIX + Colors.RESET + \ + mock_packet + self.PRINT_SUFFIX - Logger().log_packet(mockPacket) + Logger().log_packet(mock_packet) out, err = capsys.readouterr() - assert out == expectedPacket + assert out == expected_packet