예제 #1
0
 def handle_image(self, data, image_id):
     if image_id in data:
         config = CoreConfig()
         if data[image_id] == "restore":
             self.remove_existing_image(image_id)
         elif data[image_id]:
             if not data[image_id].startswith(image_id):
                 logger.warning("invalid file name: %s", data[image_id])
             else:
                 # get file extension (at least 3 characters)
                 # should be all lowercase since they are set by the upload script
                 pattern = re.compile(".*\\.([a-z]{3,})$")
                 matches = pattern.match(data[image_id])
                 if matches is None:
                     logger.warning(
                         "could not determine file extension for %s",
                         image_id)
                 else:
                     self.remove_existing_image(image_id)
                     ext = matches.group(1)
                     data_file = "{}/{}.{}".format(
                         config.get_data_directory(), image_id, ext)
                     temporary_file = "{}/{}".format(
                         config.get_temporary_directory(), data[image_id])
                     shutil.copy(temporary_file, data_file)
         del data[image_id]
         # remove any accumulated temporary files on save
         for file in glob("{}/{}*".format(config.get_temporary_directory(),
                                          image_id)):
             os.unlink(file)
예제 #2
0
 def remove_existing_image(self, image_id):
     config = CoreConfig()
     # remove all possible file extensions
     for ext in ["png", "jpg", "webp"]:
         try:
             os.unlink("{}/{}.{}".format(config.get_data_directory(),
                                         image_id, ext))
         except FileNotFoundError:
             pass
예제 #3
0
def start_receiver():
    print("""

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

Author contact info:    Jakob Ketterl, DD5JFK <*****@*****.**>
Documentation:          https://github.com/jketterl/openwebrx/wiki
Support and info:       https://groups.io/g/openwebrx

    """)

    logger.info(
        "OpenWebRX version {0} starting up...".format(openwebrx_version))

    for sig in [signal.SIGINT, signal.SIGTERM]:
        signal.signal(sig, handleSignal)

    # config warmup
    Config.validateConfig()
    coreConfig = CoreConfig()

    featureDetector = FeatureDetector()
    failed = featureDetector.get_failed_requirements("core")
    if failed:
        logger.error(
            "you are missing required dependencies to run openwebrx. "
            "please check that the following core requirements are installed and up to date: %s",
            ", ".join(failed))
        for f in failed:
            description = featureDetector.get_requirement_description(f)
            if description:
                logger.error("description for %s:\n%s", f, description)
        return 1

    # Get error messages about unknown / unavailable features as soon as possible
    # start up "always-on" sources right away
    SdrService.getAllSources()

    Services.start()

    try:
        server = ThreadedHttpServer(("0.0.0.0", coreConfig.get_web_port()),
                                    RequestHandler)
        server.serve_forever()
    except SignalException:
        pass

    WebSocketConnection.closeAll()
    Services.stop()
    SdrService.stopAllSources()
    ReportingEngine.stopAll()
    DecoderQueue.stopAll()

    return 0
예제 #4
0
 def getFilePath(self, file):
     mappedFiles = {
         "gfx/openwebrx-avatar.png": "receiver_avatar",
         "gfx/openwebrx-top-photo.jpg": "receiver_top_photo",
     }
     if file in mappedFiles and (
             "mapped" not in self.request.query
             or self.request.query["mapped"][0] != "false"):
         config = CoreConfig()
         for ext in ["png", "jpg", "webp"]:
             user_file = "{}/{}.{}".format(config.get_data_directory(),
                                           mappedFiles[file], ext)
             if os.path.exists(user_file) and os.path.isfile(user_file):
                 return user_file
     return pkg_resources.resource_filename("htdocs", file)
예제 #5
0
 def run(self):
     logger.debug("processing file %s", self.file)
     tmp_dir = CoreConfig().get_temporary_directory()
     decoder = subprocess.Popen(
         ["nice", "-n", "10"] + self.profile.decoder_commandline(self.file),
         stdout=subprocess.PIPE,
         cwd=tmp_dir,
         close_fds=True,
     )
     try:
         for line in decoder.stdout:
             self.writer.send((self.profile, self.freq, line))
     except (OSError, AttributeError):
         decoder.stdout.flush()
         # TODO uncouple parsing from the output so that decodes can still go to the map and the spotters
         logger.debug("output has gone away while decoding job.")
     try:
         rc = decoder.wait(timeout=10)
         if rc != 0:
             raise RuntimeError("decoder return code: {0}".format(rc))
     except subprocess.TimeoutExpired:
         logger.warning(
             "subprocess (pid=%i}) did not terminate correctly; sending kill signal.",
             decoder.pid)
         decoder.kill()
         raise
예제 #6
0
 def setupService(self, mode, frequency, source):
     logger.debug("setting up service {0} on frequency {1}".format(
         mode, frequency))
     # TODO selecting outputs will need some more intelligence here
     if mode == "packet":
         output = AprsServiceOutput(frequency)
     elif mode == "js8":
         output = Js8ServiceOutput(frequency)
     else:
         output = WsjtServiceOutput(frequency)
     d = Dsp(output)
     d.nc_port = source.getPort()
     center_freq = source.getProps()["center_freq"]
     d.set_offset_freq(frequency - center_freq)
     d.set_center_freq(center_freq)
     modeObject = Modes.findByModulation(mode)
     d.set_demodulator(modeObject.get_modulation())
     d.set_bandpass(modeObject.get_bandpass())
     d.set_secondary_demodulator(mode)
     d.set_audio_compression("none")
     d.set_samp_rate(source.getProps()["samp_rate"])
     d.set_temporary_directory(CoreConfig().get_temporary_directory())
     d.set_service()
     d.start()
     return d
예제 #7
0
 def __init__(self, service: bool = False):
     self.process = None
     self.tcpSource = None
     self.service = service
     self.direwolfConfigPath = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format(
         tmp_dir=CoreConfig().get_temporary_directory(), myid=id(self)
     )
     self.direwolfConfig = None
     super().__init__()
예제 #8
0
 def __init__(self, writer_id):
     self.timestamp = datetime.utcnow()
     self.writer_id = writer_id
     tmp_dir = CoreConfig().get_temporary_directory()
     self.filename = "{tmp_dir}/openwebrx-audiochopper-master-{id}-{timestamp}.wav".format(
         tmp_dir=tmp_dir,
         id=self.writer_id,
         timestamp=self.timestamp.strftime("%y%m%d_%H%M%S"),
     )
     self.waveFile = wave.open(self.filename, "wb")
     self.waveFile.setnchannels(1)
     self.waveFile.setsampwidth(2)
     self.waveFile.setframerate(12000)
예제 #9
0
 def has_codecserver_ambe(self):
     tmp_dir = CoreConfig().get_temporary_directory()
     cmd = ["mbe_synthesizer", "--test"]
     config = Config.get()
     if "digital_voice_codecserver" in config:
         cmd += ["--server", config["digital_voice_codecserver"]]
     try:
         process = subprocess.Popen(
             cmd,
             stdin=subprocess.DEVNULL,
             stdout=subprocess.DEVNULL,
             stderr=subprocess.DEVNULL,
             cwd=tmp_dir,
         )
         return process.wait() == 0
     except FileNotFoundError:
         return False
예제 #10
0
    def __init__(self, sdrSource):
        self.sdrSource = sdrSource
        super().__init__()

        stack = PropertyStack()
        stack.addLayer(0, self.sdrSource.props)
        stack.addLayer(1, Config.get())
        self.props = props = stack.filter(
            "samp_rate",
            "fft_size",
            "fft_fps",
            "fft_voverlap_factor",
            "fft_compression",
        )

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

        def set_fft_averages(changes=None):
            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.wireProperty("samp_rate", dsp.set_samp_rate),
            props.wireProperty("fft_size", dsp.set_fft_size),
            props.wireProperty("fft_fps", dsp.set_fft_fps),
            props.wireProperty("fft_compression", dsp.set_fft_compression),
            props.filter("samp_rate", "fft_size", "fft_fps",
                         "fft_voverlap_factor").wire(set_fft_averages),
        ]

        set_fft_averages()

        dsp.set_temporary_directory(CoreConfig().get_temporary_directory())
        logger.debug("Spectrum thread initialized successfully.")
예제 #11
0
 def command_is_runnable(self, command, expected_result=None):
     tmp_dir = CoreConfig().get_temporary_directory()
     cmd = shlex.split(command)
     env = os.environ.copy()
     # prevent X11 programs from opening windows if called from a GUI shell
     env.pop("DISPLAY", None)
     try:
         process = subprocess.Popen(
             cmd,
             stdin=subprocess.DEVNULL,
             stdout=subprocess.DEVNULL,
             stderr=subprocess.DEVNULL,
             cwd=tmp_dir,
             env=env,
         )
         rc = process.wait()
         if expected_result is None:
             return rc != 32512
         else:
             return rc == expected_result
     except FileNotFoundError:
         return False
예제 #12
0
    def switchFiles(self):
        with self.switchingLock:
            file = self.wavefile
            self.wavefile = self.getWaveFile()

        file.close()
        tmp_dir = CoreConfig().get_temporary_directory()

        for profile in self.profiles:
            # create hardlinks for the individual profiles
            filename = "{tmp_dir}/openwebrx-audiochopper-{pid}-{timestamp}.wav".format(
                tmp_dir=tmp_dir,
                pid=id(profile),
                timestamp=file.getTimestamp().strftime(
                    profile.getFileTimestampFormat()),
            )
            try:
                os.link(file.getFileName(), filename)
            except OSError:
                logger.exception("Error while linking job files")
                continue

            job = QueueJob(profile, self.outputWriter, filename,
                           self.dsp.get_operating_freq())
            try:
                DecoderQueue.getSharedInstance().put(job)
            except Full:
                logger.warning("decoding queue overflow; dropping one file")
                job.unlink()

        try:
            # our master can be deleted now, the profiles will delete their hardlinked copies after processing
            file.unlink()
        except OSError:
            logger.exception("Error while unlinking job files")

        self._scheduleNextSwitch()
예제 #13
0
 def __init__(self, handler, request, options):
     path = CoreConfig().get_aprs_symbols_path()
     if not path.endswith("/"):
         path += "/"
     self.path = path
     super().__init__(handler, request, options)
예제 #14
0
 def _getBookmarksFile():
     coreConfig = CoreConfig()
     return "{data_directory}/bookmarks.json".format(
         data_directory=coreConfig.get_data_directory())
예제 #15
0
파일: dsp.py 프로젝트: jasongaunt/openwebrx
    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),
            "js8_demod": Js8Parser(self.handler),
        }

        self.props = PropertyStack()

        # local demodulator properties not forwarded to the sdr
        # ensure strict validation since these can be set from the client
        # and are used to build executable commands
        validators = {
            "output_rate": "int",
            "hd_output_rate": "int",
            "squelch_level": "num",
            "secondary_mod": ModulationValidator(),
            "low_cut": "num",
            "high_cut": "num",
            "offset_freq": "int",
            "mod": ModulationValidator(),
            "secondary_offset_freq": "int",
            "dmr_filter": "int",
        }
        self.localProps = PropertyValidator(
            PropertyLayer().filter(*validators.keys()), validators)

        self.props.addLayer(0, self.localProps)
        # properties that we inherit from the sdr
        self.props.addLayer(
            1,
            self.sdrSource.getProps().filter(
                "audio_compression",
                "fft_compression",
                "digimodes_fft_size",
                "samp_rate",
                "center_freq",
                "start_mod",
                "start_freq",
                "wfm_deemphasis_tau",
                "digital_voice_codecserver",
            ),
        )

        self.dsp = 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(changes):
            if ("center_freq" not in self.props
                    or self.props["center_freq"] is None
                    or "offset_freq" not in self.props
                    or self.props["offset_freq"] is None):
                return
            freq = self.props["center_freq"] + self.props["offset_freq"]
            for parser in self.parsers.values():
                parser.setDialFrequency(freq)

        if "start_mod" in self.props:
            self.dsp.set_demodulator(self.props["start_mod"])
            mode = Modes.findByModulation(self.props["start_mod"])

            if mode and mode.bandpass:
                self.dsp.set_bpf(mode.bandpass.low_cut, mode.bandpass.high_cut)
            else:
                self.dsp.set_bpf(-4000, 4000)

        if "start_freq" in self.props and "center_freq" in self.props:
            self.dsp.set_offset_freq(self.props["start_freq"] -
                                     self.props["center_freq"])
        else:
            self.dsp.set_offset_freq(0)

        self.subscriptions = [
            self.props.wireProperty("audio_compression",
                                    self.dsp.set_audio_compression),
            self.props.wireProperty("fft_compression",
                                    self.dsp.set_fft_compression),
            self.props.wireProperty("digimodes_fft_size",
                                    self.dsp.set_secondary_fft_size),
            self.props.wireProperty("samp_rate", self.dsp.set_samp_rate),
            self.props.wireProperty("output_rate", self.dsp.set_output_rate),
            self.props.wireProperty("hd_output_rate",
                                    self.dsp.set_hd_output_rate),
            self.props.wireProperty("offset_freq", self.dsp.set_offset_freq),
            self.props.wireProperty("center_freq", self.dsp.set_center_freq),
            self.props.wireProperty("squelch_level",
                                    self.dsp.set_squelch_level),
            self.props.wireProperty("low_cut", set_low_cut),
            self.props.wireProperty("high_cut", set_high_cut),
            self.props.wireProperty("mod", self.dsp.set_demodulator),
            self.props.wireProperty("dmr_filter", self.dsp.set_dmr_filter),
            self.props.wireProperty("wfm_deemphasis_tau",
                                    self.dsp.set_wfm_deemphasis_tau),
            self.props.wireProperty("digital_voice_codecserver",
                                    self.dsp.set_codecserver),
            self.props.filter("center_freq",
                              "offset_freq").wire(set_dial_freq),
        ]

        self.dsp.set_temporary_directory(
            CoreConfig().get_temporary_directory())

        def send_secondary_config(*args):
            self.handler.write_secondary_dsp_config({
                "secondary_fft_size":
                self.props["digimodes_fft_size"],
                "if_samp_rate":
                self.dsp.if_samp_rate(),
                "secondary_bw":
                self.dsp.secondary_bw(),
            })

        def set_secondary_mod(mod):
            if mod == False:
                mod = None
            self.dsp.set_secondary_demodulator(mod)
            if mod is not None:
                send_secondary_config()

        self.subscriptions += [
            self.props.wireProperty("secondary_mod", set_secondary_mod),
            self.props.wireProperty("digimodes_fft_size",
                                    send_secondary_config),
            self.props.wireProperty("secondary_offset_freq",
                                    self.dsp.set_secondary_offset_freq),
        ]

        self.startOnAvailable = False

        self.sdrSource.addClient(self)

        super().__init__()
예제 #16
0
 def _getSettingsFile():
     coreConfig = CoreConfig()
     return "{data_directory}/settings.json".format(
         data_directory=coreConfig.get_data_directory())
예제 #17
0
파일: users.py 프로젝트: jketterl/openwebrx
 def _getUsersFile(self):
     config = CoreConfig()
     return "{data_directory}/users.json".format(
         data_directory=config.get_data_directory())
예제 #18
0
 def getFilePath(self, file=None):
     if self.file is None:
         raise FileNotFoundError("missing filename")
     return "{tmp}/{file}".format(
         tmp=CoreConfig().get_temporary_directory(), file=self.file)