Skip to content

Commit

Permalink
merged devel branch, ready for release #2
Browse files Browse the repository at this point in the history
  • Loading branch information
Marzona committed Apr 30, 2016
2 parents 822726c + f7a3a08 commit 5a1c71f
Show file tree
Hide file tree
Showing 23 changed files with 2,156 additions and 512 deletions.
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
rig-remote
===========
rig-remote devel version notes
-------------------------------

started as a fork of https://github.com/marmelo/gqrx-remote, in https://github.com/marzona/gqrx-remote I ended up adding features and rewriting all of the previous code exiting the original scope of the tool.
After sending some pull request I created this new repo with an updated project name.
Added Features
--------------

- Threaded scanning of bookmarks or frequency blocks, in "police scanner" fashion.
- Selectable infinite or limited passes.
- Selectable fixed pause on signal detection, or "wait on signal", where the scan will pause on a detected signal until the frequency is clear for a specified time.
- Lockout of selected bookmarks.
- Selectable logging of scanning activity to a file.
- On-the-fly updates of scanning parameters during active scan operation.
- Additional user input validation checks and validation of config and bookmark files.

Remotely control software radio receivers that implement rigctl protocol, like [gqrx](http://gqrx.dk/),
while keeping your bookmarks in order.
TODOs/desired enhancements are listed in the issues section.
If you find any problem feel free to create an issue, the issue will be addressed as soon as possible.

rigctl: (http://sourceforge.net/apps/mediawiki/hamlib/index.php?title=Documentation) protocol (which is [partially implemented since gqrx v2.3](http://gqrx.dk/doc/remote-control)).
All implemented features are functional and tested, but likely that bugs are hiding in there somewhere.

![rig-remote-linux](https://github.com/Marzona/rig-remote/blob/master/screenshots/rig-remote.png)

Expand Down Expand Up @@ -93,4 +100,5 @@ The file ```rig-bookmarks.csv``` consists on a standard comma-separated values f

1090000000,FM,ADBS
```
=======
23 changes: 17 additions & 6 deletions modules/app_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
Copyright (c) 2014 Rafael Marmelo
Copyright (c) 2015 Simone Marzona
Copyright (c) 2016 Tim Sweeney
TAS - Tim Sweeney - [email protected]
2016/03/21 - TAS - Validate config file entries on read.
"""

# import modules
Expand All @@ -32,7 +38,7 @@ class AppConfig(object):
"""

def __init__(self, alternate_config_file): #pragma: no cover
def __init__(self, alternate_config_file):
"""Default config, they will be overwritten when a conf is loaded
this will be used to write a default config file.
If the command line specifies a config file we note it in
Expand All @@ -48,7 +54,7 @@ def __init__(self, alternate_config_file): #pragma: no cover
self.io = IO()
self.default_config_file = ".rig-remote/rig-remote.conf"
self.config_file = None
self.config = DEFAULT_CONFIG
self.config = dict.copy(DEFAULT_CONFIG)

if alternate_config_file:
self.config_file = alternate_config_file
Expand All @@ -57,21 +63,25 @@ def __init__(self, alternate_config_file): #pragma: no cover
self.config_file = os.path.join(os.path.expanduser('~'),
self.default_config_file)

def read_conf(self): # pragma: no cover
def read_conf(self):
"""Read the configuration file.
If the default one doesn't exist we create one with sane values.
and then we re-read it.
and then we re-read it. It logs an error if a line of the file is not
valid and moves on to the next one.
:param: none
:raises: none
:returns: none
"""

if os.path.isfile(self.config_file):
logger.info("Using config file:{}".format(self.config_file))
self.io.csv_load(self.config_file, "=")
error = 0
for row in self.io.row_list:
self.config[row[0].strip()] = row[1].strip()
if len(row) == 2 and row[0].strip() in self.config.keys() :
self.config[row[0].strip()] = row[1].strip()
else:
logger.warning("Error in config file line: " + str(row))
else:
self.write_conf()
self.read_conf()
Expand Down Expand Up @@ -102,3 +112,4 @@ def write_conf(self):
row.append(self.config[key])
self.io.row_list.append(row)
self.io.csv_save(self.config_file, "=")

33 changes: 25 additions & 8 deletions modules/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Copyright (c) 2014 Rafael Marmelo
Copyright (c) 2015 Simone Marzona
Copyright (c) 2016 Tim Sweeney
"""

# constant definition
Expand All @@ -36,16 +37,15 @@
# once tuned a freq, check this number of times for a signal
SIGNAL_CHECKS=2
# time to wait between checks on the same frequency
NO_SIGNAL_DELAY = .2
NO_SIGNAL_DELAY = .1
# once we send the cmd for tuning a freq, wait this time
TIME_WAIT_FOR_TUNE = .1
TIME_WAIT_FOR_TUNE = .25
# minimum interval in hertz
MIN_INTERVAL = 1000
# fictional mode set for active frequencies
UNKNOWN_MODE = "unknown"
# monitoring mode delay
MONITOR_MODE_DELAY = 2
# monitoring mode loops count

# dictionary for mapping between rig modes and rig-remote modes
# the key is the rig-remote namings and the value is the rig naming
Expand All @@ -61,7 +61,8 @@
MODE_MAP["CWL"] = "CW-L",
MODE_MAP["CWU"] = "CW-U"

SUPPORTED_SCANNING_ACTIONS = ("start")
SUPPORTED_SCANNING_ACTIONS = ("start",
"stop")

SUPPORTED_SCANNING_MODES = ("bookmarks",
"frequency")
Expand All @@ -70,10 +71,26 @@
"port" : "7356",
"interval" : "1",
"delay" : "5",
"passes" : "0",
"sgn_level" : "-30",
"range_min" : "24,000",
"range_max" : "1800,000",
"always_on_top" : "True",
"save_exit" : "False",
"auto_bookmark" : "False",
"monitor_mode_loops" : 10}
"wait" : "false",
"record" : "false",
"log" : "false",
"always_on_top" : "true",
"save_exit" : "false",
"auto_bookmark" : "false",
"log_filename" : "rig-remote.log"}

LEN_BM = 4

class BM(object):
"Helper class with 4 attribs."

freq, mode, desc, lockout = range(LEN_BM)

LOG_FILE_NAME="rig-remote-log.txt"

UI_EVENT_TIMER_DELAY = 1000
QUEUE_MAX_SIZE = 10
107 changes: 101 additions & 6 deletions modules/disk_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,23 @@
Copyright (c) 2014 Rafael Marmelo
Copyright (c) 2015 Simone Marzona
Copyright (c) 2016 Tim Sweeney
TAS - Tim Sweeney - [email protected]
2016/02/24 - TAS - Added log file class to handle logging scanning
activity to a file.
"""

import csv
import logging
import os.path
from modules.exceptions import InvalidPathError
from modules.constants import BM
from modules.constants import LOG_FILE_NAME
import datetime
import time

# logging configuration
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -58,21 +69,20 @@ def csv_load(self, csv_file, delimiter):
"""

self._path_check(csv_file)

try:
with open(csv_file, 'r') as data_file:
reader = csv.reader(data_file, delimiter=delimiter)
for line in reader:
self.row_list.append(line)

except csv.Error:
logger.error("The file provided({})"\
" is not a file with values "\
"separated by {}.".format(csv_file, delimiter))
logger.exception("The file provided({})"\
" is not a file with values "\
"separated by {}.".format(csv_file, delimiter))

except (IOError, OSError):
logger.error("Error while trying to read the file: "\
"{}".format(csv_file))
logger.exception("Error while trying to read the file: "\
"{}".format(csv_file))

def csv_save(self, csv_file, delimiter):
"""Save current frequencies to disk.
Expand All @@ -92,3 +102,88 @@ def csv_save(self, csv_file, delimiter):
"{}".format(csv_file))


class LogFile(object):
"""Handles the a tasks of logging to a file.
"""

def __init__(self):
"""Defines the log file name and
sets the fhandler self.log_file to None.
"""

self.log_filename = LOG_FILE_NAME
self.log_file = None

def open(self, name = None):
"""Opens a log file.
:param name: log file name, defaults to None
:type name: string
"""
if name != None :
self.log_filename = name
try:
self.log_file = open(self.log_filename, 'a')
except (IOError, OSError):
logger.error("Error while trying to open log file: "\
"{}".format(self.log_filename))

def write(self, record_type, record, signal):
"""Writes a message to the log file.
:param record_type: type of the record to write
:type record_type: string
:param record: data to write
:type record: tuple
:param signal: signal level
:type signal: list
:raises IOError or OSError for any issue that happens while writing.
"""

if record_type not in ["B","F"]:
logger.error("Record type not supported, must be 'B' or 'F'"\
"got {}".format(record_type))
raise TypeError

if record_type == 'B' :
lstr = 'B ' + str(datetime.datetime.today().strftime\
("%a %Y-%b-%d %H:%M:%S")) + ' ' + \
record[BM.freq] + ' ' + record[BM.mode] + \
' ' + str(signal) + "\n"
else :
lstr = 'F ' + str(datetime.datetime.today().strftime\
("%a %Y-%b-%d %H:%M:%S")) + ' ' + \
record[2] + ' ' + record[1] + \
' ' + str(signal) + "\n"
try:
self.log_file.write(lstr)
except AttributeError:
logger.exception("No log file provided, but log feature selected.")
raise
except (IOError, OSError):
logger.exception("Error while trying to write log file: "\
"{}".format(self.log_filename))
except (TypeError, IndexError):
logger.exception("At least one of the parameter isn't of the "\
"expected type:"\
"record_type {},"\
"record {},"\
"signal {}".format(type(record_type),
type(record),
type(signal)))
raise

def close(self):
"""Closes the log file.
:raises IOError OSError: if there are issues while closing the log file
"""

if self.log_file != None :
try:
self.log_file.close()
except (IOError, OSError):
logger.error("Error while trying to close log file: "\
"{}".format(self.log_filename))

1 change: 1 addition & 0 deletions modules/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ class UnsupportedScanningConfigError(NonRetriableError):

class RetriableError (object):
pass

Loading

0 comments on commit 5a1c71f

Please sign in to comment.