Exemple #1
0
 def setParams(self, params):
     # allow direct configuration only if enabled in the config
     keys = PropertyManager.getSharedInstance()["configurable_keys"]
     if not keys:
         return
     # only the keys in the protected property manager can be overridden from the web
     protected = (
         self.sdr.getProps()
         .collect(*keys)
         .defaults(PropertyManager.getSharedInstance())
     )
     for key, value in params.items():
         protected[key] = value
Exemple #2
0
 def start():
     if not PropertyManager.getSharedInstance()["services_enabled"]:
         return
     for source in SdrService.getSources().values():
         props = source.getProps()
         if "services" not in props or props["services"] is not False:
             Services.handlers.append(ServiceHandler(source))
Exemple #3
0
    def __init__(self, conn):
        super().__init__(conn)

        pm = PropertyManager.getSharedInstance()
        self.write_config(pm.collect("google_maps_api_key", "receiver_gps", "map_position_retention_time").__dict__())

        Map.getSharedInstance().addClient(self)
Exemple #4
0
    def getConfig(self, port, is_service):
        pm = PropertyManager.getSharedInstance()

        config = """
ACHANNELS 1
ADEVICE stdin null

CHANNEL 0
MYCALL {callsign}
MODEM 1200

KISSPORT {port}
AGWPORT off
        """.format(port=port, callsign=pm["aprs_callsign"])

        if is_service and pm["aprs_igate_enabled"]:
            config += """
IGSERVER {server}
IGLOGIN {callsign} {password}
            """.format(server=pm["aprs_igate_server"],
                       callsign=pm["aprs_callsign"],
                       password=pm["aprs_igate_password"])

            if pm["aprs_igate_beacon"]:
                (lat, lon) = pm["receiver_gps"]
                lat = "{0}^{1:.2f}{2}".format(int(lat), (lat - int(lat)) * 60,
                                              "N" if lat > 0 else "S")
                lon = "{0}^{1:.2f}{2}".format(int(lon), (lon - int(lon)) * 60,
                                              "E" if lon > 0 else "W")

                config += """
PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=R lat={lat} long={lon} comment="OpenWebRX APRS gateway"
                """.format(lat=lat, lon=lon)

        return config
Exemple #5
0
    def __init__(self, id, props):
        self.id = id
        self.props = props
        self.profile_id = None
        self.activateProfile()
        self.rtlProps = self.props.collect(*self.getEventNames()).defaults(PropertyManager.getSharedInstance())
        self.wireEvents()
        self.commandMapper = None

        if "port" in props and props["port"] is not None:
            self.port = props["port"]
        else:
            self.port = getAvailablePort()
        self.monitor = None
        self.clients = []
        self.spectrumClients = []
        self.spectrumThread = None
        self.process = None
        self.modificationLock = threading.Lock()
        self.failed = False
        self.state = SdrSource.STATE_STOPPED
        self.busyState = SdrSource.BUSYSTATE_IDLE

        if self.isAlwaysOn():
            self.start()
Exemple #6
0
    def loadProps():
        if SdrService.sdrProps is None:
            pm = PropertyManager.getSharedInstance()
            featureDetector = FeatureDetector()

            def loadIntoPropertyManager(dict: dict):
                propertyManager = PropertyManager()
                for (name, value) in dict.items():
                    propertyManager[name] = value
                return propertyManager

            def sdrTypeAvailable(value):
                try:
                    if not featureDetector.is_available(value["type"]):
                        logger.error(
                            'The RTL source type "{0}" is not available. please check requirements.'
                            .format(value["type"]))
                        return False
                    return True
                except UnknownFeatureException:
                    logger.error(
                        'The RTL source type "{0}" is invalid. Please check your configuration'
                        .format(value["type"]))
                    return False

            # transform all dictionary items into PropertyManager object, filtering out unavailable ones
            SdrService.sdrProps = {
                name: loadIntoPropertyManager(value)
                for (name, value) in pm["sdrs"].items()
                if sdrTypeAvailable(value)
            }
            logger.info("SDR sources loaded. Available SDRs: {0}".format(
                ", ".join(
                    map(lambda x: x["name"], SdrService.sdrProps.values()))))
Exemple #7
0
 def getSharedInstance():
     with WsjtQueue.creationLock:
         if WsjtQueue.sharedInstance is None:
             pm = PropertyManager.getSharedInstance()
             WsjtQueue.sharedInstance = WsjtQueue(
                 maxsize=pm["wsjt_queue_length"],
                 workers=pm["wsjt_queue_workers"])
     return WsjtQueue.sharedInstance
Exemple #8
0
 def getSharedInstance():
     with PskReporter.creationLock:
         if PskReporter.sharedInstance is None:
             if PropertyManager.getSharedInstance()["pskreporter_enabled"]:
                 PskReporter.sharedInstance = PskReporter()
             else:
                 PskReporter.sharedInstance = PskReporterDummy()
     return PskReporter.sharedInstance
Exemple #9
0
 def decoding_depth(self, mode):
     pm = PropertyManager.getSharedInstance()
     # mode-specific setting?
     if "wsjt_decoding_depths" in pm and mode in pm["wsjt_decoding_depths"]:
         return pm["wsjt_decoding_depths"][mode]
     # return global default
     if "wsjt_decoding_depth" in pm:
         return pm["wsjt_decoding_depth"]
     # default when no setting is provided
     return 3
Exemple #10
0
 def __init__(self, source):
     self.source = source
     self.tmp_dir = PropertyManager.getSharedInstance(
     )["temporary_directory"]
     (self.wavefilename, self.wavefile) = self.getWaveFile()
     self.switchingLock = threading.Lock()
     self.timer = None
     (self.outputReader, self.outputWriter) = Pipe()
     self.doRun = True
     super().__init__()
Exemple #11
0
 def command_is_runnable(self, command):
     tmp_dir = PropertyManager.getSharedInstance()["temporary_directory"]
     cmd = shlex.split(command)
     try:
         process = subprocess.Popen(cmd,
                                    stdout=subprocess.DEVNULL,
                                    stderr=subprocess.DEVNULL,
                                    cwd=tmp_dir)
         return process.wait() != 32512
     except FileNotFoundError:
         return False
Exemple #12
0
    def removeOldPositions(self):
        pm = PropertyManager.getSharedInstance()
        retention = timedelta(seconds=pm["map_position_retention_time"])
        cutoff = datetime.now() - retention

        to_be_removed = [
            callsign for (callsign, pos) in self.positions.items()
            if pos["updated"] < cutoff
        ]
        for callsign in to_be_removed:
            self.removeLocation(callsign)
Exemple #13
0
 def getReceiverInformation(self):
     pm = PropertyManager.getSharedInstance()
     callsign = pm["pskreporter_callsign"]
     locator = Locator.fromCoordinates(pm["receiver_gps"])
     decodingSoftware = "OpenWebRX " + openwebrx_version
     body = [
         b for s in [callsign, locator, decodingSoftware]
         for b in self.encodeString(s)
     ]
     body = self.pad(body, 4)
     body = bytes(Uploader.receieverDelimiter +
                  list((len(body) + 4).to_bytes(2, "big")) + body)
     return body
Exemple #14
0
    def __init__(self, sdrSource):
        self.sdrSource = sdrSource
        super().__init__()

        self.props = props = self.sdrSource.props.collect(
            "samp_rate",
            "fft_size",
            "fft_fps",
            "fft_voverlap_factor",
            "fft_compression",
            "csdr_dynamic_bufsize",
            "csdr_print_bufsizes",
            "csdr_through",
            "temporary_directory",
        ).defaults(PropertyManager.getSharedInstance())

        self.dsp = dsp = csdr.dsp(self)
        dsp.nc_port = self.sdrSource.getPort()
        dsp.set_demodulator("fft")

        def set_fft_averages(key, value):
            samp_rate = props["samp_rate"]
            fft_size = props["fft_size"]
            fft_fps = props["fft_fps"]
            fft_voverlap_factor = props["fft_voverlap_factor"]

            dsp.set_fft_averages(
                int(
                    round(1.0 * samp_rate / fft_size / fft_fps /
                          (1.0 - fft_voverlap_factor))
                ) if fft_voverlap_factor > 0 else 0)

        self.subscriptions = [
            props.getProperty("samp_rate").wire(dsp.set_samp_rate),
            props.getProperty("fft_size").wire(dsp.set_fft_size),
            props.getProperty("fft_fps").wire(dsp.set_fft_fps),
            props.getProperty("fft_compression").wire(dsp.set_fft_compression),
            props.getProperty("temporary_directory").wire(
                dsp.set_temporary_directory),
            props.collect("samp_rate", "fft_size", "fft_fps",
                          "fft_voverlap_factor").wire(set_fft_averages),
        ]

        set_fft_averages(None, None)

        dsp.csdr_dynamic_bufsize = props["csdr_dynamic_bufsize"]
        dsp.csdr_print_bufsizes = props["csdr_print_bufsizes"]
        dsp.csdr_through = props["csdr_through"]
        logger.debug("Spectrum thread initialized successfully.")
Exemple #15
0
    def updateServices(self):
        logger.debug("re-scheduling services due to sdr changes")
        self.stopServices()
        if not self.source.isAvailable():
            logger.debug("sdr source is unavailable")
            return
        cf = self.source.getProps()["center_freq"]
        sr = self.source.getProps()["samp_rate"]
        srh = sr / 2
        frequency_range = (cf - srh, cf + srh)

        dials = [
            dial
            for dial in Bandplan.getSharedInstance().collectDialFrequencies(frequency_range)
            if self.isSupported(dial["mode"])
        ]

        if not dials:
            logger.debug("no services available")
            return

        with self.lock:
            self.services = []

            groups = self.optimizeResampling(dials, sr)
            if groups is None:
                for dial in dials:
                    self.services.append(self.setupService(dial["mode"], dial["frequency"], self.source))
            else:
                for group in groups:
                    frequencies = sorted([f["frequency"] for f in group])
                    min = frequencies[0]
                    max = frequencies[-1]
                    cf = (min + max) / 2
                    bw = max - min
                    logger.debug("group center frequency: {0}, bandwidth: {1}".format(cf, bw))
                    resampler_props = PropertyManager()
                    resampler_props["center_freq"] = cf
                    # TODO the + 24000 is a temporary fix since the resampling optimizer does not account for required bandwidths
                    resampler_props["samp_rate"] = bw + 24000
                    resampler = Resampler(resampler_props, self.source)
                    resampler.start()

                    for dial in group:
                        self.services.append(self.setupService(dial["mode"], dial["frequency"], resampler))

                    # resampler goes in after the services since it must not be shutdown as long as the services are still running
                    self.services.append(resampler)
Exemple #16
0
 def enrich(self, meta):
     if not PropertyManager.getSharedInstance()["digital_voice_dmr_id_lookup"]:
         return None
     if not "source" in meta:
         return None
     id = meta["source"]
     cache = DmrCache.getSharedInstance()
     if not cache.isValid(id):
         if not id in self.threads:
             self.threads[id] = threading.Thread(target=self.downloadRadioIdData, args=[id], daemon=True)
             self.threads[id].start()
         return None
     data = cache.get(id)
     if "count" in data and data["count"] > 0 and "results" in data:
         return data["results"][0]
     return None
Exemple #17
0
    def isSupported(self, mode):
        # TODO this should be in a more central place (the frontend also needs this)
        requirements = {
            "ft8": "wsjt-x",
            "ft4": "wsjt-x",
            "jt65": "wsjt-x",
            "jt9": "wsjt-x",
            "wspr": "wsjt-x",
            "packet": "packet",
        }
        fd = FeatureDetector()

        # this looks overly complicated... but i'd like modes with no requirements to be always available without
        # being listed in the hash above
        unavailable = [mode for mode, req in requirements.items() if not fd.is_available(req)]
        configured = PropertyManager.getSharedInstance()["services_decoders"]
        available = [mode for mode in configured if mode not in unavailable]

        return mode in available
Exemple #18
0
    def getSunTimes(self, date):
        pm = PropertyManager.getSharedInstance()
        lat, lng = pm["receiver_gps"]
        degtorad = math.pi / 180
        radtodeg = 180 / math.pi

        #Number of days since 01/01
        days = date.timetuple().tm_yday

        # Longitudinal correction
        longCorr = 4 * lng

        # calibrate for solstice
        b = 2 * math.pi * (days - 81) / 365

        # Equation of Time Correction
        eoTCorr = 9.87 * math.sin(
            2 * b) - 7.53 * math.cos(b) - 1.5 * math.sin(b)

        # Solar correction
        solarCorr = longCorr + eoTCorr

        # Solar declination
        declination = math.asin(math.sin(23.45 * degtorad) * math.sin(b))

        sunrise = 12 - math.acos(
            -math.tan(lat * degtorad) *
            math.tan(declination)) * radtodeg / 15 - solarCorr / 60
        sunset = 12 + math.acos(
            -math.tan(lat * degtorad) *
            math.tan(declination)) * radtodeg / 15 - solarCorr / 60

        midnight = datetime.combine(date, datetime.min.time())
        sunrise = midnight + timedelta(hours=sunrise)
        sunset = midnight + timedelta(hours=sunset)
        logger.debug("for {date} sunrise: {sunrise} sunset {sunset}".format(
            date=date, sunrise=sunrise, sunset=sunset))

        return sunrise, sunset
Exemple #19
0
    def __init__(self, conn):
        super().__init__(conn)

        self.dsp = None
        self.sdr = None
        self.configSub = None
        self.connectionProperties = {}

        try:
            ClientRegistry.getSharedInstance().addClient(self)
        except TooManyClientsException:
            self.write_backoff_message("Too many clients")
            self.close()
            raise

        pm = PropertyManager.getSharedInstance()

        self.setSdr()

        # send receiver info
        receiver_keys = [
            "receiver_name",
            "receiver_location",
            "receiver_asl",
            "receiver_gps",
            "photo_title",
            "photo_desc",
        ]
        receiver_details = dict((key, pm.getPropertyValue(key)) for key in receiver_keys)
        receiver_details["locator"] = Locator.fromCoordinates(receiver_details["receiver_gps"])
        self.write_receiver_details(receiver_details)

        self.__sendProfiles()

        features = FeatureDetector().feature_availability()
        self.write_features(features)

        CpuUsageThread.getSharedInstance().add_client(self)
Exemple #20
0
    def update(self):
        pm = PropertyManager.getSharedInstance().collect("server_hostname", "web_port", "sdrhu_key")
        data = parse.urlencode({
            "url": "http://{server_hostname}:{web_port}".format(**pm.__dict__()),
            "apikey": pm["sdrhu_key"]
        }).encode()

        res = request.urlopen("https://sdr.hu/update", data=data)
        if res.getcode() < 200 or res.getcode() >= 300:
            logger.warning('sdr.hu update failed with error code %i', res.getcode())
            return 2

        returned = res.read().decode("utf-8")
        if "UPDATE:" not in returned:
            logger.warning("Update failed, your receiver cannot be listed on sdr.hu!")
            return 2

        value = returned.split("UPDATE:")[1].split("\n", 1)[0]
        if value.startswith("SUCCESS"):
            logger.info("Update succeeded!")
        else:
            logger.warning("Update failed, your receiver cannot be listed on sdr.hu! Reason: %s", value)
        return 20
Exemple #21
0
def main():
    print("""

OpenWebRX - Open Source SDR Web App for Everyone!  | for license see LICENSE file in the package
_________________________________________________________________________________________________

Author contact info:    Jakob Ketterl, DD5JFK <*****@*****.**>

    """)

    pm = PropertyManager.getSharedInstance().loadConfig()

    featureDetector = FeatureDetector()
    if not featureDetector.is_available("core"):
        print(
            "you are missing required dependencies to run openwebrx. "
            "please check that the following core requirements are installed:")
        print(", ".join(featureDetector.get_requirements("core")))
        return

    # Get error messages about unknown / unavailable features as soon as possible
    SdrService.loadProps()

    if "sdrhu_key" in pm and pm["sdrhu_public_listing"]:
        updater = SdrHuUpdater()
        updater.start()

    Services.start()

    try:
        server = ThreadedHttpServer(
            ("0.0.0.0", pm.getPropertyValue("web_port")), RequestHandler)
        server.serve_forever()
    except KeyboardInterrupt:
        WebSocketConnection.closeAll()
        Services.stop()
        PskReporter.stop()
Exemple #22
0
 def loadIntoPropertyManager(dict: dict):
     propertyManager = PropertyManager()
     for (name, value) in dict.items():
         propertyManager[name] = value
     return propertyManager
Exemple #23
0
    def setSdr(self, id=None):
        while True:
            next = None
            if id is not None:
                next = SdrService.getSource(id)
            if next is None:
                next = SdrService.getFirstSource()
            if next is None:
                # exit condition: no sdrs available
                self.handleNoSdrsAvailable()
                return

            # exit condition: no change
            if next == self.sdr:
                return

            self.stopDsp()

            if self.configSub is not None:
                self.configSub.cancel()
                self.configSub = None

            self.sdr = next

            self.startDsp()

            # keep trying until we find a suitable SDR
            if self.sdr.getState() == SdrSource.STATE_FAILED:
                self.write_log_message('SDR device "{0}" has failed, selecting new device'.format(self.sdr.getName()))
            else:
                break

        # send initial config
        self.setDspProperties(self.connectionProperties)

        configProps = (
            self.sdr.getProps()
            .collect(*OpenWebRxReceiverClient.config_keys)
            .defaults(PropertyManager.getSharedInstance())
        )

        def sendConfig(key, value):
            config = dict((key, configProps[key]) for key in OpenWebRxReceiverClient.config_keys)
            # TODO mathematical properties? hmmmm
            config["start_offset_freq"] = configProps["start_freq"] - configProps["center_freq"]
            # TODO this is a hack to support multiple sdrs
            config["sdr_id"] = self.sdr.getId()
            self.write_config(config)

            cf = configProps["center_freq"]
            srh = configProps["samp_rate"] / 2
            frequencyRange = (cf - srh, cf + srh)
            self.write_dial_frequendies(Bandplan.getSharedInstance().collectDialFrequencies(frequencyRange))
            bookmarks = [b.__dict__() for b in Bookmarks.getSharedInstance().getBookmarks(frequencyRange)]
            self.write_bookmarks(bookmarks)

        self.configSub = configProps.wire(sendConfig)
        sendConfig(None, None)
        self.__sendProfiles()

        self.sdr.addSpectrumClient(self)
Exemple #24
0
    def __init__(self, handler, sdrSource):
        self.handler = handler
        self.sdrSource = sdrSource
        self.parsers = {
            "meta": MetaParser(self.handler),
            "wsjt_demod": WsjtParser(self.handler),
            "packet_demod": AprsParser(self.handler),
            "pocsag_demod": PocsagParser(self.handler),
        }

        self.localProps = (self.sdrSource.getProps().collect(
            "audio_compression",
            "fft_compression",
            "digimodes_fft_size",
            "csdr_dynamic_bufsize",
            "csdr_print_bufsizes",
            "csdr_through",
            "digimodes_enable",
            "samp_rate",
            "digital_voice_unvoiced_quality",
            "dmr_filter",
            "temporary_directory",
            "center_freq",
        ).defaults(PropertyManager.getSharedInstance()))

        self.dsp = csdr.dsp(self)
        self.dsp.nc_port = self.sdrSource.getPort()

        def set_low_cut(cut):
            bpf = self.dsp.get_bpf()
            bpf[0] = cut
            self.dsp.set_bpf(*bpf)

        def set_high_cut(cut):
            bpf = self.dsp.get_bpf()
            bpf[1] = cut
            self.dsp.set_bpf(*bpf)

        def set_dial_freq(key, value):
            freq = self.localProps["center_freq"] + self.localProps[
                "offset_freq"]
            for parser in self.parsers.values():
                parser.setDialFrequency(freq)

        self.subscriptions = [
            self.localProps.getProperty("audio_compression").wire(
                self.dsp.set_audio_compression),
            self.localProps.getProperty("fft_compression").wire(
                self.dsp.set_fft_compression),
            self.localProps.getProperty("digimodes_fft_size").wire(
                self.dsp.set_secondary_fft_size),
            self.localProps.getProperty("samp_rate").wire(
                self.dsp.set_samp_rate),
            self.localProps.getProperty("output_rate").wire(
                self.dsp.set_output_rate),
            self.localProps.getProperty("offset_freq").wire(
                self.dsp.set_offset_freq),
            self.localProps.getProperty("squelch_level").wire(
                self.dsp.set_squelch_level),
            self.localProps.getProperty("low_cut").wire(set_low_cut),
            self.localProps.getProperty("high_cut").wire(set_high_cut),
            self.localProps.getProperty("mod").wire(self.dsp.set_demodulator),
            self.localProps.getProperty("digital_voice_unvoiced_quality").wire(
                self.dsp.set_unvoiced_quality),
            self.localProps.getProperty("dmr_filter").wire(
                self.dsp.set_dmr_filter),
            self.localProps.getProperty("temporary_directory").wire(
                self.dsp.set_temporary_directory),
            self.localProps.collect("center_freq",
                                    "offset_freq").wire(set_dial_freq),
        ]

        self.dsp.set_offset_freq(0)
        self.dsp.set_bpf(-4000, 4000)
        self.dsp.csdr_dynamic_bufsize = self.localProps["csdr_dynamic_bufsize"]
        self.dsp.csdr_print_bufsizes = self.localProps["csdr_print_bufsizes"]
        self.dsp.csdr_through = self.localProps["csdr_through"]

        if self.localProps["digimodes_enable"]:

            def set_secondary_mod(mod):
                if mod == False:
                    mod = None
                self.dsp.set_secondary_demodulator(mod)
                if mod is not None:
                    self.handler.write_secondary_dsp_config({
                        "secondary_fft_size":
                        self.localProps["digimodes_fft_size"],
                        "if_samp_rate":
                        self.dsp.if_samp_rate(),
                        "secondary_bw":
                        self.dsp.secondary_bw(),
                    })

            self.subscriptions += [
                self.localProps.getProperty("secondary_mod").wire(
                    set_secondary_mod),
                self.localProps.getProperty("secondary_offset_freq").wire(
                    self.dsp.set_secondary_offset_freq),
            ]

        self.sdrSource.addClient(self)

        super().__init__()
Exemple #25
0
 def addClient(self, client):
     pm = PropertyManager.getSharedInstance()
     if len(self.clients) >= pm["max_clients"]:
         raise TooManyClientsException()
     self.clients.append(client)
     self.broadcast()
Exemple #26
0
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""

from owrx.sdrhu import SdrHuUpdater
from owrx.config import PropertyManager

import logging
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)

if __name__ == "__main__":
    pm = PropertyManager.getSharedInstance().loadConfig()

    if "sdrhu_public_listing" not in pm or not pm["sdrhu_public_listing"]:
        logger.error(
            'Public listing on sdr.hu is not activated. Please check "sdrhu_public_listing" in your config.'
        )
        exit(1)
    if "sdrhu_key" not in pm or pm["sdrhu_key"] is None or pm[
            "sdrhu_key"] == "":
        logger.error('Missing "sdrhu_key" in your config. Aborting')
        exit(1)
    SdrHuUpdater().update()