diff --git a/config_webrx.py b/config_webrx.py index 4fc6eab..8d7f1e8 100644 --- a/config_webrx.py +++ b/config_webrx.py @@ -79,6 +79,8 @@ ppm = 0 audio_compression="adpcm" #valid values: "adpcm", "none" fft_compression="adpcm" #valid values: "adpcm", "none" +enable_digimodes=True #If set to True, it will take more CPU per client + start_rtl_thread=True """ diff --git a/csdr.py b/csdr.py index c1e4de1..477e55f 100755 --- a/csdr.py +++ b/csdr.py @@ -77,8 +77,8 @@ class dsp: return fft_chain_base chain_begin=any_chain_base+"csdr shift_addition_cc --fifo {shift_pipe} | csdr fir_decimate_cc {decimation} {ddc_transition_bw} HAMMING | csdr bandpass_fir_fft_cc --fifo {bpf_pipe} {bpf_transition_bw} HAMMING | csdr squelch_and_smeter_cc --fifo {squelch_pipe} --outfifo {smeter_pipe} 5 1 | " if self.secondary_demodulator: - chain_begin+="tee {iqtee_pipe} | " - chain_begin+="tee {iqtee_pipe2} | " + chain_begin+="csdr tee {iqtee_pipe} | " + chain_begin+="csdr tee {iqtee2_pipe} | " chain_end = "" if self.audio_compression=="adpcm": chain_end = " | csdr encode_ima_adpcm_i16_u8" @@ -90,16 +90,12 @@ class dsp: secondary_chain_base="cat {input_pipe} | " if which == "fft": - return secondary_chain_base+"csdr fft_cc {secondary_fft_size} {secondary_fft_block_size} | csdrr logpower_cf -70 | csdr fft_one_side_ff {secondary_fft_size}" + return secondary_chain_base+"csdr realpart_cf | csdr fft_fc {secondary_fft_size} {secondary_fft_block_size} | csdr logpower_cf -70 | csdr fft_one_side_ff {secondary_fft_size}" + (" | csdr compress_fft_adpcm_f_u8 {secondary_fft_size}" if self.fft_compression=="adpcm" else "") elif which == "bpsk31": - return secondary_chain_base+"""csdr dsb_fc | \ -csdr shift_addition_cc {secondary_shift_pipe} | \ -csdr REM fir_decimate_cc {secondary_decimation} | \ + return secondary_chain_base+"""csdr shift_addition_cc {secondary_shift_pipe} | \ csdr bandpass_fir_fft_cc -{secondary_bpf_cutoff} {secondary_bpf_cutoff} {secondary_bpf_transition_bw} HAMMING | \ csdr simple_agc_cc 0.0001 0.5 | \ -csdr matched_filter_cc COSINE 48 | \ csdr timing_recovery_cc EARLYLATE {secondary_samples_per_bits} --add_q | \ - CSDR_FIXED_BUFSIZE=1 csdr realpart_cf | \ CSDR_FIXED_BUFSIZE=1 csdr binary_slicer_f_u8 | \ CSDR_FIXED_BUFSIZE=1 csdr differential_decoder_u8_u8 | \ @@ -112,37 +108,44 @@ CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8 def secondary_fft_block_size(self): return (self.samp_rate/self.decimation)/self.fft_fps - def secondary_decimation(self, which): - return 1 + def secondary_decimation(self): + return 1 #currently unused - def secondary_bpf_cutoff(self, which): + def secondary_bpf_cutoff(self): if self.secondary_demodulator == "bpsk31": - return 31.25 / self.if_samp_rate() + return (31.25/2) / self.if_samp_rate() return 0 - def secondary_bpf_transition_bw(self, which): + def secondary_bpf_transition_bw(self): if self.secondary_demodulator == "bpsk31": - return (31.25 / 2) * self.if_samp_rate() + return (31.25/2) / self.if_samp_rate() + return 0 + + def secondary_samples_per_bits(self): + if self.secondary_demodulator == "bpsk31": + return int(round(self.if_samp_rate()/31.25)) return 0 def start_secondary_demodulator(self): if(not self.secondary_demodulator): return - secondary_command_fft=secondary_chain("fft") + print "[openwebrx] starting secondary demodulator from IF input sampled at %d"%self.if_samp_rate() + secondary_command_fft=self.secondary_chain("fft") + secondary_command_demod=self.secondary_chain(self.secondary_demodulator) + self.try_create_pipes(self.secondary_pipe_names, secondary_command_demod + secondary_command_fft) + secondary_command_fft=secondary_command_fft.format( \ input_pipe=self.iqtee_pipe, \ secondary_fft_size=self.secondary_fft_size, \ secondary_fft_block_size=self.secondary_fft_block_size(), \ ) - secondary_command_demod=self.secondary_chain(self.secondary_demodulator) - secondary_command_demod.format( \ + secondary_command_demod=secondary_command_demod.format( \ input_pipe=self.iqtee2_pipe, \ secondary_shift_pipe=self.secondary_shift_pipe, \ - secondary_decimation=self.secondary_decimation(self.secondary_demodulator), \ - secondary_samples_per_bits=self.secondary_samples_per_bits(self.secondary_demodulator), \ - secondary_bpf_cutoff=self.secondary_bpf_cutoff(self.secondary_demodulator), \ - secondary_bpf_transition_bw=self.secondary_bpf_transition_bw(self.secondary_demodulator) + secondary_decimation=self.secondary_decimation(), \ + secondary_samples_per_bits=self.secondary_samples_per_bits(), \ + secondary_bpf_cutoff=self.secondary_bpf_cutoff(), \ + secondary_bpf_transition_bw=self.secondary_bpf_transition_bw() ) - try_create_pipes(secondary_command_demod + secondary_command_fft, self.secondary_pipe_names) print "[openwebrx-dsp-plugin:csdr] secondary command (fft) =", secondary_command_fft print "[openwebrx-dsp-plugin:csdr] secondary command (demod) =", secondary_command_demod @@ -151,7 +154,9 @@ CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8 #if self.csdr_dynamic_bufsize: my_env["CSDR_DYNAMIC_BUFSIZE_ON"]="1"; if self.csdr_print_bufsizes: my_env["CSDR_PRINT_BUFSIZES"]="1"; self.secondary_process_fft = subprocess.Popen(secondary_command_fft, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setpgrp, env=my_env) + print "[openwebrx-dsp-plugin:csdr] Popen on secondary command (fft)" self.secondary_process_demod = subprocess.Popen(secondary_command_demod, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setpgrp, env=my_env) + print "[openwebrx-dsp-plugin:csdr] Popen on secondary command (demod)" self.secondary_processes_running = True #open control pipes for csdr and send initialization data @@ -159,6 +164,9 @@ CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8 self.secondary_shift_pipe_file=open(self.secondary_shift_pipe,"w") self.set_secondary_offset_freq(self.secondary_offset_freq) + self.set_pipe_nonblocking(self.secondary_process_demod.stdout) + self.set_pipe_nonblocking(self.secondary_process_fft.stdout) + def set_secondary_offset_freq(self, value): self.secondary_offset_freq=value if self.secondary_processes_running: @@ -178,6 +186,9 @@ CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8 def read_secondary_fft(self, size): return self.secondary_process_fft.stdout.read(size) + def get_secondary_demodulator(self): + return self.secondary_demodulator + def set_audio_compression(self,what): self.audio_compression = what @@ -188,6 +199,10 @@ CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8 if self.fft_compression=="none": return self.fft_size*4 if self.fft_compression=="adpcm": return (self.fft_size/2)+(10/2) + def get_secondary_fft_bytes_to_read(self): + if self.fft_compression=="none": return self.secondary_fft_size*4 + if self.fft_compression=="adpcm": return (self.secondary_fft_size/2)+(10/2) + def set_samp_rate(self,samp_rate): #to change this, restart is required self.samp_rate=samp_rate @@ -273,7 +288,9 @@ CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8 return self.ddc_transition_bw_rate*(self.if_samp_rate()/float(self.samp_rate)) def try_create_pipes(self, pipe_names, command_base): + print "try_create_pipes" for pipe_name in pipe_names: + print "\t"+pipe_name if "{"+pipe_name+"}" in command_base: setattr(self, pipe_name, self.pipe_base_path+pipe_name) self.mkfifo(getattr(self, pipe_name)) @@ -287,6 +304,9 @@ CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8 try: os.unlink(pipe_path) except: print "[openwebrx-dsp-plugin:csdr] try_delete_pipes() :: unlink failed: " + pipe_path + def set_pipe_nonblocking(self, pipe): + flags = fcntl.fcntl(pipe, fcntl.F_GETFL) + fcntl.fcntl(pipe, fcntl.F_SETFL, flags | os.O_NONBLOCK) def start(self): command_base=self.chain(self.demodulator) @@ -321,7 +341,7 @@ CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8 last_decimation=self.last_decimation, fft_size=self.fft_size, fft_block_size=self.fft_block_size(), fft_averages=self.fft_averages, \ bpf_transition_bw=float(self.bpf_transition_bw)/self.if_samp_rate(), ddc_transition_bw=self.ddc_transition_bw(), \ flowcontrol=int(self.samp_rate*2), start_bufsize=self.base_bufsize*self.decimation, nc_port=self.nc_port, \ - squelch_pipe=self.squelch_pipe, smeter_pipe=self.smeter_pipe ) + squelch_pipe=self.squelch_pipe, smeter_pipe=self.smeter_pipe, iqtee_pipe=self.iqtee_pipe, iqtee2_pipe=self.iqtee2_pipe ) print "[openwebrx-dsp-plugin:csdr] Command =",command #code.interact(local=locals()) @@ -343,7 +363,7 @@ CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8 self.set_squelch_level(self.squelch_level) if self.smeter_pipe != None: self.smeter_pipe_file=open(self.smeter_pipe,"r") - fcntl.fcntl(self.smeter_pipe_file, fcntl.F_SETFL, os.O_NONBLOCK) + self.set_pipe_nonblocking(self.smeter_pipe_file) self.start_secondary_demodulator() diff --git a/htdocs/index.wrx b/htdocs/index.wrx index 12b1314..1d92b38 100644 --- a/htdocs/index.wrx +++ b/htdocs/index.wrx @@ -34,6 +34,7 @@ var waterfall_min_level_default=%[WATERFALL_MIN_LEVEL]; var waterfall_max_level_default=%[WATERFALL_MAX_LEVEL]; var waterfall_auto_level_margin=%[WATERFALL_AUTO_LEVEL_MARGIN]; + var server_enable_digimodes=%[ENABLE_DIGIMODES]; @@ -130,7 +131,7 @@ -
+
OpenWebRX client log
diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js index bd88055..1075ba3 100644 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -1165,10 +1165,15 @@ function on_ws_recv(evt) var waterfall_i16=fft_codec.decode(new Uint8Array(evt.data,4)); var waterfall_f32=new Float32Array(waterfall_i16.length-COMPRESS_FFT_PAD_N); for(var i=0;i= 0: @@ -555,6 +570,18 @@ class WebRXHandler(BaseHTTPRequestHandler): myclient.loopstat=550 dsp.start() dsp_initialized=True + elif param_name=="secondary_mod" and cfg.enable_digimodes: + if (dsp.get_secondary_demodulator() != param_value): + if dsp_initialized: dsp.stop() + if param_value == "off": + dsp.set_secondary_demodulator(None) + do_secondary_demod = False + else: + dsp.set_secondary_demodulator(param_value) + do_secondary_demod = True + if dsp_initialized: dsp.start() + elif param_name=="secondary_offset_freq" and 0 <= int(param_value) <= dsp.if_samp_rate()/2 and cfg.enable_digimodes: + dsp.set_secondary_offset_freq(int(param_value)) else: print "[openwebrx-httpd:ws] invalid parameter" if bpf_set: @@ -642,7 +669,8 @@ class WebRXHandler(BaseHTTPRequestHandler): ("%[WATERFALL_COLORS]",cfg.waterfall_colors), ("%[WATERFALL_MIN_LEVEL]",str(cfg.waterfall_min_level)), ("%[WATERFALL_MAX_LEVEL]",str(cfg.waterfall_max_level)), - ("%[WATERFALL_AUTO_LEVEL_MARGIN]","[%d,%d]"%cfg.waterfall_auto_level_margin) + ("%[WATERFALL_AUTO_LEVEL_MARGIN]","[%d,%d]"%cfg.waterfall_auto_level_margin), + ("%[ENABLE_DIGIMODES]",("true" if cfg.enable_digimodes else "false")) ) for rule in replace_dictionary: while data.find(rule[0])!=-1: