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)
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
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
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)
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
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
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__()
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)
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
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.")
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
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()
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)
def _getBookmarksFile(): coreConfig = CoreConfig() return "{data_directory}/bookmarks.json".format( data_directory=coreConfig.get_data_directory())
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__()
def _getSettingsFile(): coreConfig = CoreConfig() return "{data_directory}/settings.json".format( data_directory=coreConfig.get_data_directory())
def _getUsersFile(self): config = CoreConfig() return "{data_directory}/users.json".format( data_directory=config.get_data_directory())
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)