diff --git a/CQWW_CW_2005.fs96k.cf7040.iq.s16.dat b/CQWW_CW_2005.fs96k.cf7040.iq.s16.dat new file mode 100644 index 0000000..a4dce6e Binary files /dev/null and b/CQWW_CW_2005.fs96k.cf7040.iq.s16.dat differ diff --git a/config_webrx.py b/config_webrx.py index e956b9b..68c4ac1 100644 --- a/config_webrx.py +++ b/config_webrx.py @@ -43,7 +43,7 @@ web_port = 8073 max_clients = 20 # ==== Web GUI configuration ==== -receiver_name = "[Callsign]" +receiver_name = "OpenWebRX listing test" receiver_location = "Budapest, Hungary" receiver_asl = 200 receiver_admin = "example@example.com" @@ -65,11 +65,11 @@ Website: http://localhost # -or- # 1b. Send an email to support@kiwisdr.com and request a new listing key. # Enter the listing key from step 1a or 1b here: -listing_key = "" +listing_key = "1138" # 2. Use a public domain name or public ip address as the server hostname: -server_hostname = "localhost" +server_hostname = "openwebrx.kiwisdr.com" # 3. Set this setting to True to enable listing: -public_listing = False +public_listing = True # ==== DSP/RX settings ==== fft_fps = 9 @@ -125,6 +125,36 @@ Note: if you experience audio underruns while CPU usage is 100%, you can: # sdrs = { + "file": { + "name": "file:", + "type": "file", + "profiles": { + "40m": { + "name": "40m CQWW-CW 2005 loop", + "center_freq": 7040000, + "rf_gain": 30, + "samp_rate": 96000, + "start_freq": 7040000, + "start_mod": "lsb", + }, + }, + }, +} +''' + "kiwisdr": { + "name": "KiwiSDR test interface", + "type": "KiwiSDR", + "profiles": { + "MW": { + "name": "AM MW", + "center_freq": 1440000, + "rf_gain": 30, + "samp_rate": 12000, + "start_freq": 1440000, + "start_mod": "am", + }, + }, + }, "rtlsdr": { "name": "RTL-SDR USB Stick", "type": "rtl_sdr", @@ -243,6 +273,7 @@ sdrs = { }, }, } +''' # ==== Color themes ==== diff --git a/owrx/controllers/status.py b/owrx/controllers/status.py index 4aaa782..a493828 100644 --- a/owrx/controllers/status.py +++ b/owrx/controllers/status.py @@ -1,11 +1,13 @@ from . import Controller -from owrx.client import ClientRegistry from owrx.version import openwebrx_version from owrx.sdr import SdrService from owrx.config import Config -import os +from owrx.receiverid import ReceiverId, KeyException import json -import pkg_resources + +import logging + +logger = logging.getLogger(__name__) class StatusController(Controller): @@ -28,6 +30,11 @@ class StatusController(Controller): def indexAction(self): pm = Config.get() avatar_path = pkg_resources.resource_filename("htdocs", "gfx/openwebrx-avatar.png") + if "Authorization" in self.request.headers: + try: + ReceiverId.getResponseHeader(self.request.headers["Authorization"]) + except KeyException: + logger.exception("error processing authorization header") status = { "receiver": { "name": pm["receiver_name"], diff --git a/owrx/feature.py b/owrx/feature.py index b6d83b8..9d791a8 100644 --- a/owrx/feature.py +++ b/owrx/feature.py @@ -30,6 +30,7 @@ class FeatureDetector(object): "airspyhf": ["soapy_connector", "soapy_airspyhf"], "lime_sdr": ["soapy_connector", "soapy_lime_sdr"], "fifi_sdr": ["alsa", "rockprog"], + "file": [], "pluto_sdr": ["soapy_connector", "soapy_pluto_sdr"], "soapy_remote": ["soapy_connector", "soapy_remote"], "uhd": ["soapy_connector", "soapy_uhd"], diff --git a/owrx/http.py b/owrx/http.py index 812bec0..538bec9 100644 --- a/owrx/http.py +++ b/owrx/http.py @@ -35,19 +35,26 @@ class RequestHandler(BaseHTTPRequestHandler): logger.debug("%s - - [%s] %s", self.address_string(), self.log_date_time_string(), format % args) def do_GET(self): - self.router.route(self, "GET") + self.router.route(self, self.get_request("GET")) def do_POST(self): - self.router.route(self, "POST") + self.router.route(self, self.get_request("POST")) + + def get_request(self, method): + url = urlparse(self.path) + return Request(url, method, self.headers) class Request(object): - def __init__(self, url, method, cookies): + def __init__(self, url, method, headers): self.path = url.path self.query = parse_qs(url.query) self.matches = None self.method = method - self.cookies = cookies + self.headers = headers + self.cookies = SimpleCookie() + if "Cookie" in headers: + self.cookies.load(headers["Cookie"]) def setMatches(self, matches): self.matches = matches @@ -114,12 +121,7 @@ class Router(object): if r.matches(request): return r - def route(self, handler, method): - url = urlparse(handler.path) - cookies = SimpleCookie() - if "Cookie" in handler.headers: - cookies.load(handler.headers["Cookie"]) - request = Request(url, method, cookies) + def route(self, handler, request): route = self.find_route(request) if route is not None: controller = route.controller diff --git a/owrx/receiverid.py b/owrx/receiverid.py new file mode 100644 index 0000000..f2d9efe --- /dev/null +++ b/owrx/receiverid.py @@ -0,0 +1,63 @@ +import re +import logging +from owrx.config import Config + +logger = logging.getLogger(__name__) + + +keyRegex = re.compile("^([a-zA-Z]+)-([0-9a-f]{32})-([0-9a-f]{64})$") +keyChallengeRegex = re.compile("^([a-zA-Z]+)-([0-9a-f]{32})-([0-9a-f]{32})$") +headerRegex = re.compile("^ReceiverId (.*)$") + + +class KeyException(Exception): + pass + + +class Key(object): + def __init__(self, keyString): + matches = keyRegex.match(keyString) + if not matches: + raise KeyException("invalid key format") + self.source = matches.group(1) + self.id = matches.group(2) + self.secret = matches.group(3) + + +class KeyChallenge(object): + def __init__(self, challengeString): + matches = keyChallengeRegex.match(challengeString) + if not matches: + raise KeyException("invalid key challenge format") + self.source = matches.group(1) + self.id = matches.group(2) + self.challenge = matches.group(3) + + +class KeyResponse(object): + def __str__(self): + return "TODO" + + +class ReceiverId(object): + @staticmethod + def getResponseHeader(requestHeader): + matches = headerRegex.match(requestHeader) + if not matches: + raise KeyException("invalid authorization header") + challenge = KeyChallenge(matches.group(1)) + key = ReceiverId.findKey(challenge) + # TODO sign challenge and respond + + @staticmethod + def findKey(challenge): + def parseKey(keyString): + try: + return Key(keyString) + except KeyError as e: + logger.error(e) + keys = [key for key in (parseKey(keyString) for keyString in Config.get()['receiver_keys']) if key is not None] + matching_keys = [key for key in keys if key.source == challenge.source and key.id == challenge.id] + if matching_keys: + return matching_keys[0] + return None diff --git a/owrx/source/fifi_sdr.py b/owrx/source/fifi_sdr.py index fa97d89..69babf4 100644 --- a/owrx/source/fifi_sdr.py +++ b/owrx/source/fifi_sdr.py @@ -17,10 +17,10 @@ class FifiSdrSource(DirectSource): return super().getEventNames() + ["device"] def getFormatConversion(self): - return ["csdr convert_s16_f", "csdr gain_ff 30"] + return ["csdr convert_s16_f", "csdr gain_ff 5"] def sendRockProgFrequency(self, frequency): - process = Popen(["rockprog", "--vco", "-w", "--", "freq={}".format(frequency / 1E6)]) + process = Popen(["rockprog", "--vco", "-w", "--freq={}".format(frequency / 1E6)]) process.communicate() rc = process.wait() if rc != 0: diff --git a/owrx/source/file.py b/owrx/source/file.py new file mode 100644 index 0000000..b631a1c --- /dev/null +++ b/owrx/source/file.py @@ -0,0 +1,21 @@ +from owrx.command import Option +from .direct import DirectSource +from subprocess import Popen + +import logging + +logger = logging.getLogger(__name__) + + +class FileSource(DirectSource): + def getCommandMapper(self): + file = "CQWW_CW_2005.fs96k.cf7040.iq.s16.dat" + bytes_per_sample = 4 + #sdrProps = getProps() + #srate = sdrProps["samp_rate"] + srate = 96000 + cmd = "(while true; do cat {fn}; done) | csdr flowcontrol {sr} 20".format(fn=file,sr=srate*bytes_per_sample*1.05) + return super().getCommandMapper().setBase(cmd) + + def getFormatConversion(self): + return ["csdr convert_s16_f --bigendian", "csdr iq_swap_ff"]