Skip to content

Commit

Permalink
v6.10
Browse files Browse the repository at this point in the history
  • Loading branch information
llinkz committed Apr 25, 2020
1 parent b7d0434 commit 6ca1cfe
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 100 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# directTDoA v6.00
# directTDoA v6.10

This software is JUST a python 2/3 GUI designed to compute TDoA runs on shortwave radio transmissions using remote (GPS enabled) KiwiSDR receivers around the World.

Expand All @@ -12,7 +12,7 @@ Download [directTDoA-windows.zip](https:/llinkz/directTDoA/releases/

Then double-click on `directTDoA.bat`

#### IMPORTANT: This is the only way to launch the program due to files path problems that may occur.
#### IMPORTANT: You must use this method to launch the program to avoid file path issues.

> Info: this archive contains all the necessary files already patched and compiled and also includes light versions of GNU Octave and python, so no need to install the full versions of the last two on your machine. The unzipped archive is 272 MB, compared to ~2 GB in the other installer way.
Expand Down Expand Up @@ -94,7 +94,8 @@ Install GNU Octave in Terminal : `brew install octave`
* v5.10: removed KiwiSDR nodes "names" from .db files + compute_ultimate script has been transformed into GUI with plot_iq now only displaying the selected nodes + adding command arguments to trim_iq.py script (./trim_iq.py -h ,for help)
* v5.20: "directTDoA_v5.xx" now displayed on the KiwiSDR target's userlist when connected + simplification of the node rec-list management + adding a checkbox to automatically start (or not) _compute_ultimate.py_ script when "Stop Rec" button is clicked + extra command on KiwiSDR first line popup to add the node even if it has _fixes_min=0_
+ new _has_gps_ routine in both _plot_iq.py_ & _compute_ultimate.py_ + bug fix: regexp wrongly detecting LON + bug fix: if Sorcerer TCP client checkbox is unchecked while recording, no more endless record session.
* v6.00: Listen mode (AM/LSB/USB) is back, to get it working you must apply a patch: `patch -i kiwirecorder_patch.diff ./kiwiclient/kiwirecorder.py` from directTDoA dir and installing sounddevice + samplerate python modules with `python -m pip install sounddevice samplerate` + some python 2 Vs. python 3 bug fixes + bug fixed on map update process + .desktop files creation removed
* v6.00: Listen mode (AM/LSB/USB) is back, to get it working you must apply a patch: `patch -i kiwirecorder_patch.diff ./kiwiclient/kiwirecorder.py` from directTDoA dir and install sounddevice + samplerate python modules with `python -m pip install sounddevice samplerate` + some python 2 Vs. python 3 bug fixes + bug fixed on map update process + .desktop files creation removed
* v6.10: Restart GUI routine modified + less CMD windows for Windows OS users (using pythonw instead of python) + bug fix that caused the map to move suddenly far away when selecting a node (problem only noticed on Windows OS) + no more auto-PlotIQ() start on ultimateTDoA runs + modifications of the .bat files for Windows OS users (CPU affinity of the python processes now set towards a single one, the allocation on several generated a delay in the starting of the IQ records)
## Thanks
* Christoph Mayer @ https:/hcab14/TDoA for the main TDoA code, excellent work and thanks for the public release !
* John Seamons, KiwiSDR developper @ https:/jks-prv
Expand Down
11 changes: 4 additions & 7 deletions compute_ultimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,14 @@ def __str__(self):
@staticmethod
def run():
""" GUI Restart routine. """
global PROC_PID
try: # to kill octave-cli process if exists
os.kill(PROC_PID, signal.SIGTERM)
except (NameError, OSError):
pass
if platform.system() == "Windows":
os.execv(sys.executable, [sys.executable] + sys.argv)
os.execlp("pythonw.exe", "pythonw.exe", "compute_ultimate.py")
else:
APP.destroy()
subprocess.call([sys.executable, os.path.abspath(__file__)])
os.execv(sys.executable, [sys.executable] + sys.argv)


class ReadKnownPointFile(object):
Expand Down Expand Up @@ -161,7 +159,7 @@ def run(self):
APP.gui.writelog("Click and Drag : Select a time portion of the IQ record that you want to keep.")
APP.gui.writelog("Click to close window : No changes on the entire IQ recording.")
APP.gui.writelog("One click on the spectrogram OR less than 2 seconds selected : Deletes the IQ recording.")
subprocess.call(['python', 'trim_iq.py'], cwd=self.tdoa_rootdir, shell=False)
subprocess.call([sys.executable, 'trim_iq.py'], cwd=self.tdoa_rootdir, shell=False)
Restart().run()


Expand Down Expand Up @@ -1096,7 +1094,7 @@ def purgenode(self):

def start_stop_tdoa(self):
""" Actions to perform when Compute button is clicked. """
global tdoa_in_progress
global tdoa_in_progress, PROC_PID
global plot_kiwi_json_new, use_constraints_new, algo_new
global lon_min_map, lon_max_map, lat_min_map, lat_max_map
global plot_kiwi_json_origin, use_constraints_origin, algo_origin
Expand Down Expand Up @@ -1190,7 +1188,6 @@ def __init__(self):

def on_closing():
""" Actions to perform when software is closed using the top-right check button. """
global PROC_PID
try: # to kill octave
os.kill(PROC_PID, signal.SIGTERM)
except (NameError, OSError):
Expand Down
144 changes: 56 additions & 88 deletions directTDoA.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
from tkinter.colorchooser import askcolor
from tkinter.simpledialog import askstring, askinteger

VERSION = "directTDoA v6.00"
VERSION = "directTDoA v6.10"


class Restart(object):
Expand All @@ -71,8 +71,10 @@ def run():
os.kill(kiwisdrclient_pid, signal.SIGTERM)
except (NameError, OSError):
pass
APP.destroy()
subprocess.call([sys.executable, os.path.abspath(__file__)])
if platform.system() == "Windows":
os.execlp("pythonw.exe", "pythonw.exe", "directTDoA.py")
else:
os.execv(sys.executable, [sys.executable] + sys.argv)


class ReadKnownPointFile(object):
Expand Down Expand Up @@ -114,7 +116,7 @@ def read_cfg():
global BGC, FGC, GRAD, THRES, CONS_B, CONS_F, STAT_B, STAT_F, MAP_BOX
global TCPHOST, TCPPORT, IQDURATION, PKJ, UC, TDOAVERSION
try:
# Read the config file v5.0 format and declare variables
# Read the config file and declare variables
with open('directTDoA.cfg', 'r') as config_file:
CFG = json.load(config_file, object_pairs_hook=OrderedDict)
DX0, DX1 = CFG["map"]["x0"], CFG["map"]["x1"]
Expand All @@ -131,65 +133,13 @@ def read_cfg():
TCPHOST, TCPPORT, IQDURATION = CFG["tcp"]["host"], CFG["tcp"]["port"], CFG["tcp"]["duration"]
PKJ, UC, TDOAVERSION = CFG["iq"]["pkj"], CFG["iq"]["uc"], CFG["iq"]["mode"]
except (ImportError, ValueError):
# If an old config file format is detected, convert it to v5.0 format
with open('directTDoA.cfg', "r") as old_config_file:
configline = old_config_file.readlines()
CFG = {'map': {}, 'nodes': {}, 'iq': {}, 'guicolors': {}, 'tcp': {}, 'presets(x0/y1/x1/y0)': {}}
CFG["map"]["x0"] = configline[3].split(',')[0]
CFG["map"]["x1"] = configline[3].split(',')[2]
CFG["map"]["y0"] = configline[3].split(',')[1]
CFG["map"]["y1"] = configline[3].replace("\n", "").split(',')[3]
CFG["map"]["file"] = configline[5].split('\n')[0]
CFG["map"]["iconsize"] = 2
CFG["map"]["icontype"] = 1
CFG["map"]["mapfl"] = int(configline[7].replace("\n", "")[0])
CFG["map"]["std"] = configline[13].replace("\n", "").split(',')[0]
CFG["map"]["fav"] = configline[13].replace("\n", "").split(',')[1]
CFG["map"]["blk"] = configline[13].replace("\n", "").split(',')[2]
CFG["map"]["poi"] = configline[13].replace("\n", "").split(',')[3]
CFG["map"]["hlt"] = "#ffffff"
if configline[9] == "\n":
CFG["nodes"]["whitelist"] = []
else:
CFG["nodes"]["whitelist"] = configline[9].replace("\n", "").split(',')
if configline[11] == "\n":
CFG["nodes"]["blacklist"] = []
else:
CFG["nodes"]["blacklist"] = configline[11].replace("\n", "").split(',')
CFG["iq"]["bw"] = "4000"
CFG["iq"]["pjk"] = 0
CFG["iq"]["uc"] = 1
CFG["iq"]["mode"] = "standard"
CFG["tcp"]["host"] = "127.0.0.1"
CFG["tcp"]["port"] = 55555
CFG["tcp"]["duration"] = 12
CFG["guicolors"]["main_b"] = "#d9d9d9"
CFG["guicolors"]["main_f"] = "#000000"
CFG["guicolors"]["cons_b"] = "#000000"
CFG["guicolors"]["cons_f"] = "#00ff00"
CFG["guicolors"]["stat_b"] = "#ffffff"
CFG["guicolors"]["stat_f"] = "#000000"
CFG["guicolors"]["grad"] = 10
CFG["guicolors"]["thres"] = 186
CFG["presets(x0/y1/x1/y0)"]["EU"] = [-12, 72, 50, 30]
CFG["presets(x0/y1/x1/y0)"]["AF"] = [-20, 40, 55, -35]
CFG["presets(x0/y1/x1/y0)"]["ME"] = [25, 45, 65, 10]
CFG["presets(x0/y1/x1/y0)"]["SAM"] = [-85, 15, -30, -60]
CFG["presets(x0/y1/x1/y0)"]["O"] = [110, -10, 180, -50]
CFG["presets(x0/y1/x1/y0)"]["EAS"] = [73, 55, 147, 15]
CFG["presets(x0/y1/x1/y0)"]["CAM"] = [-120, 33, -50, 5]
CFG["presets(x0/y1/x1/y0)"]["SEAS"] = [85, 30, 155, -12]
CFG["presets(x0/y1/x1/y0)"]["SAS"] = [60, 39, 100, 4]
CFG["presets(x0/y1/x1/y0)"]["NAM"] = [-170, 82, -50, 13]
CFG["presets(x0/y1/x1/y0)"]["WR"] = [27, 72, 90, 40]
CFG["presets(x0/y1/x1/y0)"]["ER"] = [90, 82, 180, 40]
CFG["presets(x0/y1/x1/y0)"]["US"] = [-125, 50, -66, 23]
CFG["presets(x0/y1/x1/y0)"]["W"] = [-179, 89, 179, -59]
copyfile("directTDoA.cfg", "directTDoA.do-not-use-anymore.cfg")
with open('directTDoA.cfg', 'w') as config_f:
json.dump(OrderedDict(sorted(CFG.items(), key=lambda t: t[0])), config_f, indent=2)
config_f.close()
sys.exit("v4.20 config file format has been converted to v5.xx\nRestart the GUI now")
# If config file is not valid json
tkMessageBox.showinfo(title=" ¯\\_(ツ)_/¯", message="config file syntax error !")
sys.exit()
except OSError:
# If config file is not found
tkMessageBox.showinfo(title=" ¯\\_(ツ)_/¯", message="config file not found !")
sys.exit()


class SaveCfg(object):
Expand Down Expand Up @@ -421,7 +371,7 @@ def __init__(self):
def run(self):
run_dir = os.path.join('TDoA', 'iq') + os.sep + starttime + tdoa_mode + str(FREQUENCY) + os.sep
with open(os.devnull, 'w') as fp:
subprocess.call(['python', 'plot_iq.py'], cwd=os.path.join(run_dir), shell=False, stdout=fp)
subprocess.call([sys.executable, 'plot_iq.py'], cwd=os.path.join(run_dir), shell=False, stdout=fp)


class ComputeUltimate(threading.Thread):
Expand All @@ -433,7 +383,7 @@ def __init__(self):
def run(self):
run_dir = os.path.join('TDoA', 'iq') + os.sep + starttime + tdoa_mode + str(FREQUENCY)
with open(os.devnull, 'w') as fp:
subprocess.call(['python', 'compute_ultimate.py'], cwd=os.path.join(run_dir), shell=False, stdout=fp)
subprocess.call([sys.executable, 'compute_ultimate.py'], cwd=os.path.join(run_dir), shell=False, stdout=fp)


class OctaveProcessing(threading.Thread):
Expand All @@ -447,16 +397,19 @@ def run(self):
octave_errors = [b'index-out-of-bounds', b'< 2 good stations found', b'Octave:nonconformant - args',
b'n_stn=2 is not supported', b'resample.m: p and q must be positive integers',
b'Octave:invalid-index', b'incomplete \'data\' chunk']
if sys.version_info[0] == 2:
tdoa_filename = "proc_tdoa_" + KHZ_FREQ + ".m"
proc = subprocess.Popen(['octave-cli', tdoa_filename], cwd=os.path.join('TDoA'),
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE, shell=False)
else:
tdoa_filename = "proc_tdoa_" + KHZ_FREQ
proc = subprocess.Popen(['octave-cli', '--eval', tdoa_filename], cwd=os.path.join('TDoA'),
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE, shell=False)
tdoa_filename = "proc_tdoa_" + KHZ_FREQ + ".m"
proc = subprocess.Popen(['octave-cli', tdoa_filename], cwd=os.path.join('TDoA'), stderr=subprocess.STDOUT,
stdout=subprocess.PIPE, shell=False)
# if sys.version_info[0] == 2:
# tdoa_filename = "proc_tdoa_" + KHZ_FREQ + ".m"
# proc = subprocess.Popen(['octave-cli', tdoa_filename], cwd=os.path.join('TDoA'),
# stderr=subprocess.STDOUT,
# stdout=subprocess.PIPE, shell=False)
# else:
# tdoa_filename = "proc_tdoa_" + KHZ_FREQ
# proc = subprocess.Popen(['octave-cli', '--eval', tdoa_filename], cwd=os.path.join('TDoA'),
# stderr=subprocess.STDOUT,
# stdout=subprocess.PIPE, shell=False)
PROC_PID = proc.pid
logfile = open(os.path.join('TDoA', 'iq') + os.sep + starttime + tdoa_mode + str(
FREQUENCY) + os.sep + "TDoA_" + KHZ_FREQ + "_log.txt", 'w')
Expand Down Expand Up @@ -571,13 +524,12 @@ def run(self):
try:
socket2_connect = subprocess.Popen(
[sys.executable, 'kiwiclient' + os.sep + 'microkiwi_waterfall.py', '-s', self.s_host, '-p',
self.s_port],
stdout=PIPE, shell=False)
self.s_port], stdout=PIPE, shell=False)
APP.gui.writelog("Retrieving " + self.s_host + " waterfall, please wait")
while True:
snr_output = socket2_connect.stdout.readline()
if b"received sample" in snr_output:
APP.gui.console_window.insert('end -1c', '.')
# if b"received sample" in snr_output:
# APP.gui.console_window.insert('end -1c', '.')
if b"SNR" in snr_output:
APP.gui.console_window.delete('end -1c linestart', END)
APP.gui.console_window.insert('end', '\n')
Expand Down Expand Up @@ -1326,8 +1278,11 @@ def move_from(self, event):

def move_to(self, event):
""" Move to. """
self.canvas.scan_dragto(event.x, event.y, gain=1)
self.show_image() # redraw the image
if 'HOST' in globals() and "current" not in self.canvas.gettags(self.canvas.find_withtag(CURRENT))[0]:
pass
elif "current" in self.canvas.gettags(self.canvas.find_withtag(CURRENT))[0]:
self.canvas.scan_dragto(event.x, event.y, gain=1)
self.show_image() # redraw the image

def wheel(self, event):
""" Routine for mouse wheel actions. """
Expand Down Expand Up @@ -2216,8 +2171,18 @@ def start_stop_tdoa(self):
self.start_rec_button.configure(text="Start recording", state="normal")
self.start_tdoa_button.configure(text="", state="disabled")
self.purge_button.configure(state="normal")
os.kill(PROC_PID, signal.SIGTERM) # kills the octave process
os.system("killall -9 gs") # and ghostscript
try: # kills the octave process
os.kill(PROC_PID, signal.SIGTERM)
except (NameError, OSError):
pass
try: # and ghostscript
if platform.system() == "Windows":
if "gs.exe" in os.popen("tasklist").read():
os.system("taskkill /F /IM gs.exe")
else:
os.system("killall -9 gs")
except (NameError, OSError):
pass
self.writelog("Octave process has been aborted...")
for wavfiles in glob.glob(os.path.join('TDoA', 'iq') + os.sep + "*.wav"):
os.remove(wavfiles)
Expand Down Expand Up @@ -2362,7 +2327,7 @@ def create_m_file(self):

# Adapt the getmap.py arguments if a known place has been set or not
if platform.system() == "Windows":
python_path = '..\\\python\\\python.exe'
python_path = '..\\\python\\\pythonw.exe'
else:
python_path = 'python'
if selectedlat == "" or selectedlon == "":
Expand Down Expand Up @@ -2401,7 +2366,7 @@ def create_m_file(self):
os.chmod(run_dir + "compute_ultimate.py", 0o777)
os.chmod(run_dir + "plot_iq.py", 0o777)
os.chmod(run_dir + "trim_iq.py", 0o777)
PlotIQ().start()
# PlotIQ().start()
else:
copyfile(proc_m_name + ".m", run_dir + "proc_tdoa_" + KHZ_FREQ + ".m")
if platform.system() == "Windows":
Expand All @@ -2410,12 +2375,13 @@ def create_m_file(self):
:: This script moves *.wav back to iq directory and proc_tdoa_""" + KHZ_FREQ + """.m to
:: TDoA directory then opens a file editor so you can modify .m file parameters.
@echo off
if not exist *spec.pdf ..\..\..\python\python.exe plot_iq.py
set PATH=%CD%\..\..\..\octave\\bin;%CD%\..\..\..\python;%PATH%
if not exist *spec.pdf pythonw.exe plot_iq.py
copy *.wav ..\\
copy proc_tdoa_""" + KHZ_FREQ + """.m ..\..\\
cd ..\..
start /W notepad "proc_tdoa_""" + KHZ_FREQ + """.m"
..\octave\\bin\octave-cli.exe proc_tdoa_""" + KHZ_FREQ + """.m
octave-cli.exe proc_tdoa_""" + KHZ_FREQ + """.m
del proc_tdoa_""" + KHZ_FREQ + """.m""")
recompute.close()
copyfile('plot_iq.py', run_dir + "plot_iq.py")
Expand All @@ -2430,8 +2396,10 @@ def create_m_file(self):
cp proc_tdoa_""" + KHZ_FREQ + """.m ../../
cd ../..
$EDITOR proc_tdoa_""" + KHZ_FREQ + """.m
octave-cli """ + ("--eval " if sys.version_info[0] == 3 else "") + """proc_tdoa_""" + KHZ_FREQ + (".m" if sys.version_info[0] == 2 else "") + """
octave-cli proc_tdoa_""" + KHZ_FREQ + """.m
rm -f proc_tdoa_""" + KHZ_FREQ + """.m""")
# octave-cli """ + ("--eval " if sys.version_info[0] == 3 else "") + """proc_tdoa_""" + KHZ_FREQ
# + (".m" if sys.version_info[0] == 2 else "") + """
recompute.close()
os.chmod(run_dir + "recompute.sh", 0o777)
copyfile('plot_iq.py', run_dir + "plot_iq.py")
Expand Down
2 changes: 1 addition & 1 deletion directTDoA_knownpoints.db
Original file line number Diff line number Diff line change
Expand Up @@ -1902,7 +1902,7 @@ Miami (United States of America),25.7876,-80.2241
Atlanta (United States of America),33.83,-84.3999
Chicago (United States of America),41.83,-87.7501
Los_Angeles (United States of America),33.99,-118.18
Washington,_D.C. (United States of America),38.8995,-77.0094
Washington_DC (United States of America),38.8995,-77.0094
New_York (United States of America),40.75,-73.98
Montevideo (Uruguay),-34.858,-56.1711
Shahrisabz (Uzbekistan),39.0618,66.8315
Expand Down
3 changes: 2 additions & 1 deletion trim_iq.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import glob
import shutil
import platform
import sys
import argparse
import io
from io import BytesIO
Expand Down Expand Up @@ -170,7 +171,7 @@ def convert_iq_and_plot_from_mem(in_file):
# Run plot_iq.py to get the spectrogram pdf file using the new trimmed files if requested by user
if ARGS.plot_iq == "y":
with open(os.devnull, 'w') as fp:
subprocess.call(['python', 'plot_iq.py'], shell=False, stdout=fp)
subprocess.call([sys.executable, 'plot_iq.py'], shell=False, stdout=fp)

# Show the spectrogram pdf file if requested by user
if ARGS.show_result == "y":
Expand Down

0 comments on commit 6ca1cfe

Please sign in to comment.