diff --git a/grafics/openwebtx.png b/grafics/openwebtx.png
new file mode 100755
index 0000000..12fbddc
Binary files /dev/null and b/grafics/openwebtx.png differ
diff --git a/htdocs/index.html b/htdocs/index.html
new file mode 100644
index 0000000..2777afe
--- /dev/null
+++ b/htdocs/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/htdocs/owt.css b/htdocs/owt.css
new file mode 100644
index 0000000..06dba89
--- /dev/null
+++ b/htdocs/owt.css
@@ -0,0 +1,44 @@
+#owt_head
+{
+ display: inline-block;
+ padding: 0px;
+ vertical-align: top;
+ margin-top: 20px;
+}
+#owt_head a
+{
+ font-size: 11px;
+ text-decoration: underline;
+ color: #ccccff;
+}
+#owt_pids
+{
+ width: 200px;
+}
+.owt_pid
+{
+ width: calc(10% - 10px);
+ height: 5px;
+ display: inline-block;
+ margin-right: 1px;
+ background-color: #e6e7e8;
+}
+#owt_button
+{
+ display: inline-block
+}
+#owt_ptt
+{
+ background: #ff0000;
+}
+@media only screen and (max-height: 400px) {
+ #owt_head
+ {
+ margin-top: 3px !important;
+ }
+ #owt_ptt
+ {
+ margin-right: 20px;
+ margin-left: 10px;
+ }
+}
diff --git a/htdocs/owt.js b/htdocs/owt.js
new file mode 100644
index 0000000..973f320
--- /dev/null
+++ b/htdocs/owt.js
@@ -0,0 +1,207 @@
+var owt_protocol = 'ws://';
+if(window.location.toString().indexOf('https://') == 0){
+ owt_protocol = 'wss://';
+}
+owt_url1=owt_protocol+window.location.href.split("://")[1]
+owt_lastslash = owt_url1.lastIndexOf('/');
+owt_firstslash= owt_url1.indexOf('/',6);
+owt_url2=owt_url1.substr(0,owt_firstslash)
+//owt_url2=owt_url1.substr(0,owt_lastslash)
+owt_ws_url=owt_url2+"/tx/ws/";
+owt_button_orig=""
+var owt_webSocket;
+var owt_mediaRecorder
+function owt_start_ws(start_audio) {
+ owt_webSocket = new WebSocket(owt_ws_url);
+ owt_webSocket.binaryType = 'blob';
+
+ owt_webSocket.onmessage= function (event) {
+ console.log('Message from server ', event.data);
+ owt_make_msg(event.data);
+ }
+ owt_webSocket.onclose =function (event) {
+ console.log('ws closed :(', event.data);
+ owt_make_button_orig();
+ owt_make_msg('TX connection closed!');
+ owt_destroy_ws()
+ }
+ if(start_audio) {
+ //owt_webSocket.onopen =owt_start_audio();
+ if (MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')) {
+ mimeType="audio/ogg;codecs=opus";
+ } else {
+ mimeType="audio/webm;codecs=opus";
+ }
+
+ //function owt_start_audio(){
+ owt_webSocket.onopen = event => {
+ console.log("[open] Audio Node-RED websocket connection established");
+ navigator.mediaDevices
+ .getUserMedia({ audio: true, video: false, channelCount: 1, sampleRate: 12000, sampleSize: 16 })
+ .then(stream => {
+ owt_mediaRecorder = new MediaRecorder(stream, {
+ //audioBitsPerSecond : 128000,
+ audioBitsPerSecond : 64000,
+ //mimeType: 'audio/webm;codecs=opus',
+ //mimeType: 'audio/ogg;codecs=opus',
+ mimeType: mimeType,
+ });
+ owt_mediaRecorder.addEventListener('dataavailable', event => {
+ if (event.data.size > 0) {
+ //console.log(event.data.length)
+ owt_webSocket.send(event.data);
+ }
+ });
+ owt_mediaRecorder.start(100);
+ });
+ }
+ }
+}
+function owt_stop_ws() {
+ track=owt_mediaRecorder.stream.getAudioTracks()
+ track.forEach(function(track) { track.stop(); });
+
+ owt_webSocket.close()
+ owt_make_button_orig()
+}
+function owt_destroy_ws() {
+ if (typeof owt_mediaRecorder === "object" ) {
+ a=owt_mediaRecorder.stream.getAudioTracks();
+ owt_mediaRecorder.stream.removeTrack(a[0]);
+ owt_mediaRecorder.removeEventListener('dataavilable',Element)
+ owt_mediaRecorder.stop()
+ owt_mediaRecorder=0
+ console.log("audio destroyed")
+ }
+ owt_webSocket=0;
+ console.log("ws destroyed")
+}
+function owt_make_button_running() {
+ //PTT button, close link
+ document.getElementById('owt_button').innerHTML="PTT
disconnect"
+}
+function owt_make_button_orig() {
+ //connect button from variable
+ document.getElementById('owt_button').innerHTML=owt_button_orig
+}
+function owt_make_msg(data) {
+//if data contains config:, status: => ignore
+//else if containts msg: => split and print
+ document.getElementById('owt_msg').innerHTML=""
+//else print
+}
+function owt_start() {
+ //owt_vumeter()
+ owt_button_orig= document.getElementById('owt_button').innerHTML
+ owt_start_ws(1);
+ //owt_start_audio();
+ owt_make_button_running();
+}
+
+function owt_ptt_on() {
+ mute=false;
+ toggleMute();
+ owt_webSocket.send("SET:PTT=1")
+ document.getElementById('owt_ptt').style.background="#fff"
+ document.getElementById('owt_ptt').style.color="#f00"
+}
+function owt_ptt_off() {
+ mute=true;
+ toggleMute();
+ owt_webSocket.send("SET:PTT=0")
+ document.getElementById('owt_ptt').style.background="#f00"
+ document.getElementById('owt_ptt').style.color="#fff"
+}
+//keypress
+//keydown
+//keyup
+//left 37
+//right 39
+//up 38
+//down 40
+document.addEventListener('keydown', function(e) {
+ var keynum = e.keyCode || e.which;
+ console.log(keynum)
+ if(keynum == 32) {
+ owt_ptt_on()
+ }
+});
+document.addEventListener('keyup', function(e) {
+ var keynum = e.keyCode || e.which;
+ console.log("up"+keynum)
+ if(keynum == 32) {
+ owt_ptt_off()
+ }
+});
+document.addEventListener('keypress', function(e) {
+ var keynum = e.keyCode || e.which;
+ console.log("press"+keynum)
+// if(keynum == 32) {
+// owt_ptt_off()
+// }
+});
+
+//https://stackoverflow.com/questions/33322681/checking-microphone-volume-in-javascript
+function owt_vumeter() {
+ document.getElementById('owt_pids').innerHTML=`
+
+
+
+
+
+ `;
+
+ navigator.mediaDevices.getUserMedia({
+ audio: true,
+ video: false,
+ })
+ .then(function(stream) {
+ const audioContext = new AudioContext();
+ const analyser = audioContext.createAnalyser();
+ const microphone = audioContext.createMediaStreamSource(stream);
+ const scriptProcessor = audioContext.createScriptProcessor(2048, 1, 1);
+ analyser.smoothingTimeConstant = 0.8;
+ analyser.fftSize = 1024;
+
+ microphone.connect(analyser);
+ analyser.connect(scriptProcessor);
+ scriptProcessor.connect(audioContext.destination);
+ scriptProcessor.onaudioprocess = function() {
+ const array = new Uint8Array(analyser.frequencyBinCount);
+ analyser.getByteFrequencyData(array);
+ const arraySum = array.reduce((a, value) => a + value, 0);
+ const average = arraySum / array.length;
+ //console.log(Math.round(average));
+ owt_colorPids(average);
+ };
+ })
+ .catch(function(err) {
+ /* handle the error */
+ console.error(err);
+ });
+}
+function owt_colorPids(vol) {
+ //console.log('vu'+vol);
+ const allPids = [...document.querySelectorAll('.owt_pid')];
+ const numberOfPidsToColor = Math.round(vol / 20);
+ const pidsToColor = allPids.slice(0, numberOfPidsToColor);
+ for (const pid of allPids) {
+ pid.style.backgroundColor = "#e6e7e8";
+ }
+ for (const pid of pidsToColor) {
+ //console.log(pid[i]);
+ pid.style.backgroundColor = "#00ce00";
+ }
+}
+
+
+
+//TODO
+// //on websocket rx
+// display MSG (on air, problem [rigctl, high-swr, other user took over], ....)
+// on websocket close => display msg
+// fkt:
+// display msg
+// get parameters from ws-rx and update gui if gui present
+// send parameters from gui to ws (relay shift, ctcss, power, external IO)
+
diff --git a/tx.py b/tx.py
new file mode 100644
index 0000000..280ba9a
--- /dev/null
+++ b/tx.py
@@ -0,0 +1,635 @@
+#!/usr/bin/python3
+"""
+
+ This file is part of OpenWeb-TX (owt),
+ an open-source web transceiver software with a web UI.
+ Copyright (c) 2013-2015 by Andras Retzler
+ Copyright (c) 2022 by LSP/ARCG
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ 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 .
+
+
+
+https://gist.github.com/artizirk/04eb23d957d7916c01ca632bb27d5436
+https://websockets.readthedocs.io/en/stable/index.html
+https://stackoverflow.com/questions/53689602/python-3-websockets-server-http-server-run-forever-serve-forever
+
+
+TODO:
+ script for putting webrx into dummy mode if 1h without openwebrx
+"""
+import code
+import importlib
+import _thread
+import time
+import datetime
+import subprocess
+import os
+from socketserver import ThreadingMixIn
+import fcntl
+import random
+import threading
+import sys
+import traceback
+from collections import namedtuple
+
+import ctypes
+import multiprocessing
+import psutil
+
+import signal
+import socket
+from functools import reduce
+
+#httpnew 202201
+import asyncio
+import functools
+import websockets
+from http import HTTPStatus
+
+#pypy compatibility
+try: import dl
+except: pass
+try: import __pypy__
+except: pass
+pypy="__pypy__" in globals()
+
+
+config_web_port=8074
+config_audio_fifo="test_fifo"
+#config_audio_out_cmd= "opusdec --force-wav - - | aplay -D hw:1,0 -"
+#config_audio_out_cmd= "ffmpeg -y -i - -ac 2 -f alsa hw:1,0"
+config_audio_out_cmd= "ffmpeg -y -re -flags low_delay -thread_queue_size 1024 -i - -ac 2 -f alsa hw:1,0"
+#config_audio_out_cmd= "ffmpeg -y -i - -ac 2 -f pulse '0'"
+#config_audio_out_cmd= "ffmpeg -y -i - -filter_complex \"[0:a][0:a]amerge=inputs=2[a]\" -map \"[a]\" -c 2 -f alsa hw:1,0"
+ #"ffmpeg -y -i - -ac 1 -f wav - | paplay --latency-msec 1"
+ #https://stackoverflow.com/questions/66843134/windows-ffmpeg-send-audio-to-sound-cards-output ffplay?
+ #"mpv -"
+ #"ffmpeg -y -ac 1 -i - -f wav - | paplay --latency-msec 1"
+ #"cat /dev/stdin >> test2.opus"
+ #ffmpeg -loglevel debug -y -ac 1 -i test2.opus -f wav - | paplay
+ #"cat "+config_audio_fifo+" | "
+ #cmd decoding audio + playback on system
+ #signal-chain gets input from stdin
+config_rig={}
+config_rig['interface']= "/dev/ttyUSB0" #serial Port of CAT interface of TRX
+config_rig['baud']= 9600 #baud rate of serial port
+config_rig['mod_fm']= "FM 0" #modulation set for FM-TX
+config_rig['mod_lsb']= "LSB 0" #modulation set for LSB-TX
+config_rig['mod_usb']= "USB 0" #modulation set for USB-TX
+#config_rig['rigctl_cmd']= "rigctld -m 1022 -s 9600 -r /dev/ttyUSB0 -P RTS -t 4532" #FT857
+config_rig['rigctl_cmd']= "rigctld -m 1023 -s 9600 -r /dev/ttyUSB0 -P RTS -t 4532" #FT897
+#config_rig['rigctl_cmd']= "rigctld -m 1023 -s 9600 -r /dev/ttyUSB0 -P RTS -t 4532" #FT817
+ #"rigctld -m 120 -s 9600 -r /dev/ttyACM0 --dcd-type=NONE -P RTS" #FT817
+ #command to start rigctl software
+ # rigctl -l lists TRX list für '-m'
+
+config_cmd={}
+config_cmd['TX']= "/home/pi/rf-route.sh tx" #cmd executed on TX /PTT-ON
+config_cmd['RX']= "/home/pi/rf-route.sh rx" #cmd executed on RX /PTT-OFF
+#config_cmd['TX_PWR_ON']= "echo 'on'" #cmd executed on Session start /TX-Power-ON
+config_cmd['TX_PWR_ON']= "/home/pi/rf-route.sh on" #cmd executed on Session start /TX-Power-ON
+#config_cmd['TX_PWR_OFF']= "echo 'off'" #cmd executed on Session start /TX-Power-OFF
+config_cmd['TX_PWR_OFF']= "/home/pi/rf-route.sh off" #cmd executed on Session start /TX-Power-OFF
+
+
+
+
+global openwebrx
+openwebrx={} # init for parameter handling
+openwebrx['modulation']= "nfm"
+global session_sempathor
+session_semaphor= 0
+
+
+def handle_signal_to_del(sig, frame):
+ global spectrum_dsp
+ if sig == signal.SIGUSR1:
+ print("[openwebrx] Verbose status information on USR1 signal")
+ print()
+ print("time.time() =", time.time())
+ print("clients_mutex.locked() =", clients_mutex.locked())
+ print("clients_mutex_locker =", clients_mutex_locker)
+ if server_fail: print("server_fail = ", server_fail)
+ print("spectrum_thread_watchdog_last_tick =", spectrum_thread_watchdog_last_tick)
+ print()
+ print("clients:",len(clients))
+ for client in clients:
+ print()
+ for key in client._fields:
+ print("\t%s = %s"%(key,str(getattr(client,key))))
+ elif sig == signal.SIGUSR2:
+ code.interact(local=globals())
+ else:
+ print("[openwebrx] Ctrl+C: aborting.")
+ cleanup_clients(True)
+ spectrum_dsp.stop()
+ os._exit(1) #not too graceful exit
+
+## function start_openwebrx
+#
+# function for openwebrx thread, starts openwebrx
+# reads stdin from stdout of openwebrx for frequency and modulation
+def start_openwebrx():
+ global openwebrx
+ p = subprocess.Popen(['python3 -u openwebrx.py 2>&1 '],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=0,
+ shell=True,
+ stdin=None,
+ universal_newlines=True,
+ cwd="../")
+ modb=""
+ print("startopenwebrx")
+ for line in iter(p.stdout.readline, b''):
+ # Sampling at 2400000 S/s.
+ # Tuned to 144250000 Hz.
+ # [openwebrx-httpd:ws,1] command: SET mod=nfm low_cut=-4000 high_cut=4000 offset_freq=0
+ # csdr_s shift_addition_cc: reinitialized to 0.128407
+ # csdr_s bandpass_fir_fft_cc: filter initialized, low_cut = -0.24875, high_cut = -0.00704792
+
+ if "sampling_rate:" in line:
+ sampl= line.split("sampling_rate:",1)[1]
+ sampl= int(sampl)
+ openwebrx['samplerate']=sampl
+ print ("S:"+str(sampl))
+
+ elif "center_freq:" in line:
+ freq_cent= line.split("center_freq:",1)[1]
+ freq_cent= int(freq_cent)
+ openwebrx['centerfreq']=freq_cent
+ print ("T:"+str(freq_cent))
+
+ elif "SET mod=" in line:
+ moda= line.split("SET mod=",1)[1]
+ modb= moda.split(" ")[0]
+ print ("M:"+modb)
+
+ elif "csdr_s shift_addition_cc: reinitialized to" in line:
+ offset= float(line.split("cc: reinitialized to",1)[1])
+ openwebrx['offset']=offset
+ print ("s:"+str(offset) )
+
+ if modb == "ssb":
+ if "csdr_s bandpass_fir_fft_cc: filter initialized," in line:
+ low_cuta= line.split("low_cut = ",1)[1]
+ low_cut= low_cuta.split(",",1)[0]
+ high_cut= line.split("high_cut = ",1)[1]
+ #print("low_cut"+low_cut)
+ #print("high_cut"+high_cut)
+ low=abs(float(low_cut))
+ high=abs(float(high_cut))
+ if (low > high):
+ mod="lsb"
+ print("lsb")
+ else:
+ mod="usb"
+ print("usb")
+ openwebrx['modulation']=mod
+ else:
+ openwebrx['modulation']=modb
+
+## function asyncWorker
+#
+# loop to handle anything for tx operation that might consume time and thereby
+# delay audio samples,
+# quick and dirty communication over global variables "aw_*"
+def asyncWorker():
+ global process_audio, process_rigctl, rigctl_thread, socket_rigctl
+ global aw_trx_on, aw_trx_off, aw_rx, aw_tx, aw_ptt_on, aw_ptt_off
+ #global vars, tcp client, rigctl server
+ #start and end rigctl
+ #connect tcp client to rigtctl
+ #on / off tx
+ #rx / tx switch
+ #anything else that needs time
+ aw_trx_on= 0
+ aw_trx_off= 0
+ aw_rx= 0
+ aw_tx= 0
+ aw_ptt_on= 0
+ aw_ptt_off= 0
+ while True:
+ a=0
+ #if tx schould be switched on
+ # wait x start rigctl
+ if aw_trx_on:
+ aw_trx_on= 0
+ print("AW: trx on")
+ try:
+ process_txpwr = subprocess.Popen((config_cmd['TX_PWR_ON']), stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+ except:
+ pass
+ #### BEGIN RIGCTL #####
+ try:
+ process_rigctl = subprocess.Popen([config_rig['rigctl_cmd']], stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+ except:
+ pass
+ time.sleep(1.2)
+ try:
+ rigctl_pid= process_rigctl.pid
+ rigctl_thread=psutil.Process(rigctl_pid)
+ time.sleep(1.8)
+ print("rigctl started")
+ socket_rigctl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ server_address = ('127.0.0.1', 4532)
+ print('rigctld connecting to {} port {}'.format(*server_address))
+ socket_rigctl.connect(server_address)
+ socket_rigctl.sendall(b'M LSB 0\n\n')
+ print("AW: trx on")
+ except:
+ pass
+ #if tx off, take care of rigctl
+ #end tcp client
+ if aw_trx_off:
+ aw_trx_off= 0
+ try:
+ socket_rigctl.sendall(("T 0\n").encode())
+ time.sleep(1)
+ socket_rigctl.close()
+ except:
+ pass
+ if rigctl_thread.is_running():
+ children=rigctl_thread.children(recursive=True)
+ for child in children:
+ try:
+ child.kill()
+ except:
+ pass
+ process_txpwr = subprocess.Popen((config_cmd['TX_PWR_OFF']), stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+ print("AW: trx off")
+
+ #set tx, relais,
+ if aw_tx:
+ aw_tx= 0
+ process_tx = subprocess.Popen((config_cmd['TX']), stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+ print("AW: relais to TRX")
+
+
+ #set rx, relais
+ if aw_rx:
+ aw_rx= 0
+ process_tx = subprocess.Popen((config_cmd['RX']), stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+ print("AW: relais to SDR")
+
+ #set ptt on, relais, rigctl
+ if aw_ptt_on:
+ aw_ptt_on= 0
+ process_tx = subprocess.Popen((config_cmd['TX']), stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+ time.sleep(0.27)
+ socket_rigctl.sendall(("T 1\n").encode())
+ print("AW: ptt on")
+
+ #set ptt of, rigctl, relais
+ if aw_ptt_off:
+ aw_ptt_off=0
+ socket_rigctl.sendall(("T 0\n").encode())
+ time.sleep(0.15)
+ process_tx = subprocess.Popen((config_cmd['RX']), stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+ print("AW: ptt off")
+
+
+ #if relais only, to it
+ time.sleep(0.01)
+
+
+
+receiver_failed=spectrum_thread_watchdog_last_tick=rtl_thread=spectrum_dsp=server_fail=None
+
+## function main
+#
+# main function, everything gets started here
+# openwebrx
+# asynchron worker
+# webserver
+# websocket
+def main():
+ global process_audio
+ #global clients, clients_mutex, pypy, lock_try_time, avatar_ctime, cfg, logs
+ #global serverfail, rtl_thread, spectrum_thread, ws_kill, sdr_selected, spectrum_kill
+
+ #Change process name to "openwebrx" (to be seen in ps)
+ try:
+ for libcpath in ["/lib/i386-linux-gnu/libc.so.6","/lib/libc.so.6"]:
+ if os.path.exists(libcpath):
+ libc = dl.open(libcpath)
+ libc.call("prctl", 15, "openwebrx-tx", 0, 0, 0)
+ break
+ except:
+ pass
+
+ #check for external programs #todo, add rigctld, ffmpeg, ...
+ if os.system("csdr_s 2> /dev/null") == 32512: #check for csdr
+ print("[openwebrx-main] You need to install \"csdr_s\" to run OpenWebRX!\n")
+ return
+ if os.system("nmux_s --help 2> /dev/null") == 32512: #check for nmux
+ print("[openwebrx-main] You need to install an up-to-date version of \"csdr\" that contains the \"nmux\" tool to run OpenWebRX! Please upgrade \"csdr\"!\n")
+ return
+ #if start_sdr() == False: #moved out for interactive sdr change
+ # return
+
+ #Initialize clients
+ clients=[]
+
+ #threading.Thread(target = measure_thread_function, args = ()).start()
+
+ #### BEGIN OPENWEBRX ####
+ thread_openwebrx = threading.Thread(target = start_openwebrx, args= ()).start()
+ #### END OPENWEBRX ####
+
+ #### BEGIN AsynWorker ####
+ thread_asyncWorker = threading.Thread(target = asyncWorker, args= ()).start()
+ #### END AsyncWorker ####
+
+
+ ####Start HTTP and WS ####
+ # set first argument for the handler to current working directory
+ handler = functools.partial(process_request, os.getcwd())
+ start_server = websockets.serve(ws_process, "0.0.0.0", config_web_port,
+ process_request=handler)
+ print("Running server at http://127.0.0.1 port:"+str(config_web_port))
+
+ asyncio.get_event_loop().run_until_complete(start_server)
+ try:
+ print("running")
+ asyncio.get_event_loop().run_forever()
+ except KeyboardInterrupt:
+ print("server crashed")
+
+
+def send_302_to_del(self,what):
+ self.send_response(302)
+ self.send_header('Content-type','text/html')
+ self.send_header("Location", "{0}".format(what))
+ self.end_headers()
+ mime='text/html'
+ data="Object moved
Please click here to continue.".format(what)
+
+ response_headers = [
+ ('Server', 'asyncio websocket server'),
+ ('Connection', 'close'),
+ ]
+
+ response_headers.append(('Content-Type', mime))
+ response_headers.append(("Location", "{0}".format(what)))
+
+ # Read the whole file into memory and send it out
+ #body = open(full_path, 'rb').read()
+ response_headers.append(('Content-Length', str(len(data))))
+ return HTTPStatus.FOUND, response_headers, data
+
+
+ #def do_GET(self):
+
+## function freq_shift
+#
+# decides if frequency shift for repeater operation is needed
+# returns new frequency
+def freq_shift(f,mod):
+ if (mod == "fm"):
+ #10m -100e3
+ if (f >= 29620e3 and f<29690e3):
+ f=f-100e3
+ return f
+
+ #6m -600e3
+ if (f >= 51810e3 and f<51990e3):
+ f=f-600e3
+ return f
+
+ #2m -600e3
+ if (f >= 145500e3 and f<146000e3):
+ f=f-600e3
+ return f
+
+ #70cmi -7.6e6
+ if (f >= 438,6375e6 and f<439.6e6):
+ f=f-7.6e6
+ return f
+ #439.8..400 is 9.4
+
+ #23cm -28e6
+ if (f >= 1298.025e6 and f<1298.975e6):
+ f=f-28e6
+ return f
+ else:
+ return f
+
+## function write_data
+#
+# sends http response for http server
+def write_data(path,mime_type,data): #makes http header and content
+ #data=data.encode()
+ response_headers = [
+ ('Server', 'asyncio websocket server'),
+ ('Connection', 'close'),
+ ]
+
+ response_headers.append(('Content-Type', mime_type))
+
+ response_headers.append(('Content-Length', str(len(data))))
+ return HTTPStatus.OK, response_headers, data
+
+## function ws_process
+#
+# handles http Websocket requests
+# main task compressed audio => stdout, must not be delayed!
+async def ws_process(websocket, path):
+ global dsp_plugin, clients_mutex, clients, avatar_ctime, sw_version, receiver_failed, ws_kill, sdr_selected, process_rigctl
+ global session_semaphor
+ global aw_trx_on, aw_trx_off, aw_rx, aw_tx, aw_ptt_on, aw_ptt_off
+
+ global process_audio, rigctl_thread, socket_rigctl, openwebrx
+ print("New WebSocket connection from", websocket.remote_address)
+
+ path=path.replace("..","")
+ path_temp_parts=path.split("?")
+ path=path_temp_parts[0]
+ client_address= websocket.remote_address[0]
+ session_is_mine= 0 #1 if this is the active session
+ try:
+ if path[:4]=="/ws/":
+ loopstat=0
+ print("[openwebrx-ws] Client requested WebSocket connection")
+ try:
+ #check if rigctl is still running, othervise return
+ #TODO send error ws-msg to client
+ if session_semaphor == 0 and path[:4]=="/ws/":
+ session_semaphor= 1
+ session_is_mine= 1
+ #elif path[:4] != "/wc/":
+ # return
+ if session_is_mine :
+ process_audio = subprocess.Popen((config_audio_out_cmd), stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+
+ print("process_audio started")
+ #switch tx power on
+ aw_trx_on= 1
+ else:
+ print("WS wrong session, semaphor!")
+ raise ValueError('WS wrong session, semphor!')
+ #send default parameters
+ #startstr=("MSG center_freq={0} bandwidth={1} fft_size={2} fft_fps={3} audio_compression={4} fft_compression={5} max_clients={6} sdr={7} setup".format(str(cfg.shown_center_freq[sdr_selected]),str(cfg.samp_rate[sdr_selected]),cfg.fft_size,cfg.fft_fps,cfg.audio_compression,cfg.fft_compression,cfg.max_clients,sdr_selected)).encode()
+
+ while True:
+
+ try:
+ rdata= await asyncio.wait_for(websocket.recv(), timeout=0.01)
+ except asyncio.TimeoutError:
+ rdata = 0
+
+ if (rdata != 0):
+
+ if rdata[:3]=="SET":
+ print(rdata)
+ pairs=rdata[4:].split(" ")
+ for pair in pairs:
+ param_name, param_value = pair.split("=")
+ if param_name == "PTT" and session_is_mine:
+ if process_rigctl.poll() :
+ print("no rigctl!")
+ raise ValueError('No rigctl!')
+ return
+
+ print("WS start pttW")
+ freq= float(openwebrx['centerfreq'])-float(openwebrx['samplerate'])*float(openwebrx['offset'])
+ mod=""
+ if (openwebrx['modulation'] == "nfm"):
+ mod= config_rig['mod_fm']
+ elif (openwebrx['modulation'] == "lsb"):
+ mod= config_rig['mod_lsb']
+ elif (openwebrx['modulation'] == "usb"):
+ mod= config_rig['mod_usb']
+ if (mod != ""): #if modulation valid
+ print("F "+str(freq))
+ print("center "+str(openwebrx['centerfreq']))
+ print("samp "+str(openwebrx['samplerate']))
+ print("offset "+str(openwebrx['offset']))
+ #TODO
+ if (int(param_value)):
+ freq=freq_shift(freq,mod)
+ socket_rigctl.sendall(("F "+str(freq)+"\n").encode())
+ socket_rigctl.sendall(("M "+mod+"\n").encode())
+ aw_ptt_on= 1
+ else:
+ aw_ptt_off=1
+ else:
+ error_msg="modulation not valid for TX"
+ print(error_msg)
+ elif param_name == "low_cut" and -filter_limit <= int(param_value) <= filter_limit:
+ a=3
+ #TODO
+ # repater shift, enable/disable
+ # power
+ else:
+ print("[openwebrx-httpd:ws] invalid parameter")
+ else:
+ process_audio.stdin.write(rdata)
+
+ except:
+ if session_is_mine:
+ process_audio.stdin.close()
+ process_audio.send_signal(signal.SIGINT)
+ aw_trx_off= 1
+ session_semaphor= 0
+ process_audio = subprocess.Popen(("killall -9 ffmpeg"), stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ print("[openwebrx-httpd:ws] exception: ",exc_type,exc_value)
+ traceback.print_tb(exc_traceback) #TODO digimodes
+ if exc_value[0]==32: #"broken pipe", client disconnected
+ print("broken ws, client disconnected")
+ elif exc_value[0]==11: #"resource unavailable" on recv, client disconnected
+ print("resource unavailable on recv")
+ else:
+ print ("[openwebrx-httpd] error in /ws/ handler: ",exc_type,exc_value)
+
+ try:
+ print("do_GET /ws/ delete disconnected")
+ if session_is_mine:
+ process_audio.stdin.close()
+ process_audio.send_signal(signal.SIGINT)
+ aw_trx_off= 1
+ session_semaphor= 0
+ process_audio = subprocess.Popen(("killall -9 ffmpeg"), stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ except:
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ print("[openwebrx-httpd:ws] client cannot be closed: ",exc_type,exc_value)
+ traceback.print_tb(exc_traceback)
+ finally:
+ cmr()
+ return
+ except:
+ print("[openwebrx-httpd:ws] error (@outside)")
+ if session_is_mine:
+ process_audio.stdin.close()
+ process_audio.send_signal(signal.SIGINT)
+ aw_trx_off= 1
+ session_semaphor= 0
+ process_audio = subprocess.Popen(("killall -9 ffmpeg"), stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True )
+
+ return
+
+
+## function process request
+#
+# handles http GET requests
+async def process_request(sever_root, path, request_headers):
+ global dsp_plugin, clients_mutex, clients, avatar_ctime, sw_version, receiver_failed
+ if "Upgrade" in request_headers:
+ return # Probably a WebSocket connection
+ #return write_data(path,'text/html','bla')
+ rootdir = 'htdocs'
+ mime_type= 'text/html'
+ path=path.replace("..","")
+ path_temp_parts=path.split("?")
+ path=path_temp_parts[0]
+ user_agent= request_headers._list[1][1] # 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0'
+ client_address=request_headers._list[0][1] #'127.0.0.1:8073'
+ request_param=path_temp_parts[1] if(len(path_temp_parts)>1) else ""
+ try:
+ if path=="/" or path=="":
+ path="/index.html"
+ # there's even another cool tip at http://stackoverflow.com/questions/4419650/how-to-implement-timeout-in-basehttpserver-basehttprequesthandler-python
+
+ else:
+ f=open(rootdir+path, "rb")
+ data=f.read()
+ extension=path[(len(path)-4):len(path)]
+ extension=extension[2:] if extension[1]=='.' else extension[1:]
+ if(("wrx","html","htm","html").count(extension)):
+ mime_type= 'text/html'
+ elif(extension=="js"):
+ mime_type= 'text/javascript'
+ elif(extension=="css"):
+ mime_type= 'text/css'
+ f.close()
+ return write_data(path,mime_type,data)
+
+ return
+ except IOError:
+ #self.send_error(404, 'Invalid path.')
+ return HTTPStatus.NOT_FOUND, [], b'404 NOT FOUND'
+
+ except:
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ print("[openwebrx-httpd] error (@outside):", exc_type, exc_value)
+ traceback.print_tb(exc_traceback)
+
+if __name__=="__main__":
+ main()
+