mirror of
https://github.com/Lukas0025/YAGS.git
synced 2025-04-25 00:01:24 +01:00
Added CW decoder
This commit is contained in:
parent
340bd22900
commit
b7e2531a1a
@ -1,3 +1,6 @@
|
|||||||
install:
|
install:
|
||||||
cp tools/baseband_spectogram.py /usr/local/bin/
|
cp tools/baseband_spectogram.py /usr/local/bin/
|
||||||
chmod +x /usr/local/bin/baseband_spectogram.py
|
chmod +x /usr/local/bin/baseband_spectogram.py
|
||||||
|
|
||||||
|
cp tools/cw_morse.py /usr/local/bin/
|
||||||
|
chmod +x /usr/local/bin/cw_morse.py
|
@ -50,7 +50,11 @@ class recorder(threading.Thread):
|
|||||||
|
|
||||||
realStart = int(datetime.datetime.now(tz=datetime.timezone.utc).timestamp())
|
realStart = int(datetime.datetime.now(tz=datetime.timezone.utc).timestamp())
|
||||||
|
|
||||||
os.system(f"satdump record {baseband} --source {self.job['receiver']['params']['radio']} --samplerate {fs} --frequency {self.job['transmitter']['centerFrequency']} --gain {self.job['receiver']['params']['gain']} --baseband_format s8 --timeout {recordTime}")
|
ret = os.system(f"satdump record {baseband} --source {self.job['receiver']['params']['radio']} --samplerate {fs} --frequency {self.job['transmitter']['centerFrequency']} --gain {self.job['receiver']['params']['gain']} --baseband_format s8 --timeout {recordTime}")
|
||||||
|
|
||||||
|
if ret != 0: # fail to open sdr
|
||||||
|
puller.setFail(self.job["id"])
|
||||||
|
return
|
||||||
|
|
||||||
realEnd = int(datetime.datetime.now(tz=datetime.timezone.utc).timestamp())
|
realEnd = int(datetime.datetime.now(tz=datetime.timezone.utc).timestamp())
|
||||||
|
|
||||||
|
145
station/tools/cw_morse.py
Normal file
145
station/tools/cw_morse.py
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# Simple CW morse decoder
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cliParser = argparse.ArgumentParser(description='Simple CW morse decoder')
|
||||||
|
|
||||||
|
cliParser.add_argument('input_file', type=str, help='input filename baseband record')
|
||||||
|
cliParser.add_argument('output_file', type=str, help='output filename decoded text')
|
||||||
|
cliParser.add_argument('-fs', '--sampleRate', type=float, help='sets the sample rate [hz]')
|
||||||
|
cliParser.add_argument('-mfs', '--morseFS', type=float, help='sets the sample rate for CW (Downsample) [hz]', default=125000)
|
||||||
|
cliParser.add_argument('-of', '--offsetFreq', type=float, help='sets carrier offset [hz]', default=0)
|
||||||
|
cliParser.add_argument('-bw', '--bandWidth', type=float, help='sets carrier bandwidth [hz]', default=60)
|
||||||
|
cliParser.add_argument('-fc', '--failChar', type=str, help='sets char what is used when morse decode fail', default='')
|
||||||
|
|
||||||
|
|
||||||
|
cliParser.add_argument('-f', '--format', type=str,
|
||||||
|
help='Output format',
|
||||||
|
choices=["int8"],
|
||||||
|
default='int8')
|
||||||
|
|
||||||
|
args = cliParser.parse_args()
|
||||||
|
|
||||||
|
data = np.memmap(args.input_file, dtype=args.format, mode="r")
|
||||||
|
|
||||||
|
fft_size = 1024
|
||||||
|
|
||||||
|
sample_rate = args.sampleRate
|
||||||
|
down_rate = args.morseFS
|
||||||
|
offset_freq = args.offsetFreq
|
||||||
|
bandwidth = args.bandWidth
|
||||||
|
failChar = args.failChar
|
||||||
|
|
||||||
|
downSample = int(sample_rate // down_rate)
|
||||||
|
|
||||||
|
data = data[1::2] + 1j * data[0::2]
|
||||||
|
data = data[1::downSample] # downsample
|
||||||
|
data = data - np.mean(data) # normalize
|
||||||
|
|
||||||
|
num_rows = len(data) // fft_size
|
||||||
|
|
||||||
|
beep_trashold = []
|
||||||
|
|
||||||
|
for i in range(num_rows):
|
||||||
|
fft = np.abs(np.fft.fftshift(np.fft.fft(data[i*fft_size:(i+1)*fft_size])))
|
||||||
|
frame_max = np.max(fft[len(fft)//2 - bandwidth//2 + offset_freq : len(fft)//2 + bandwidth//2 + offset_freq])
|
||||||
|
beep_trashold.append(frame_max)
|
||||||
|
|
||||||
|
beep_trashold = np.mean(beep_trashold)
|
||||||
|
|
||||||
|
beep = np.zeros(num_rows)
|
||||||
|
|
||||||
|
for i in range(num_rows):
|
||||||
|
fft = np.abs(np.fft.fftshift(np.fft.fft(data[i*fft_size:(i+1)*fft_size])))
|
||||||
|
frame_max = np.max(fft[len(fft)//2 - bandwidth//2 + offset_freq : len(fft)//2 + bandwidth//2 + offset_freq])
|
||||||
|
if (frame_max >= beep_trashold):
|
||||||
|
beep[i] = 1
|
||||||
|
|
||||||
|
is_one = False
|
||||||
|
mean_one_len = []
|
||||||
|
mean_zero_len = []
|
||||||
|
sig_len = 0
|
||||||
|
|
||||||
|
for signal in beep:
|
||||||
|
if signal == 1 and not(is_one):
|
||||||
|
mean_zero_len.append(sig_len)
|
||||||
|
sig_len = 0
|
||||||
|
is_one = True
|
||||||
|
|
||||||
|
if signal == 0 and is_one:
|
||||||
|
mean_one_len.append(sig_len)
|
||||||
|
sig_len = 0
|
||||||
|
is_one = False
|
||||||
|
|
||||||
|
sig_len += 1
|
||||||
|
|
||||||
|
mean_one_len = np.mean(mean_one_len)
|
||||||
|
mean_zero_len = np.mean(mean_zero_len)
|
||||||
|
|
||||||
|
is_one = False
|
||||||
|
morse = ""
|
||||||
|
sig_len = 0
|
||||||
|
|
||||||
|
for signal in beep:
|
||||||
|
if signal == 1 and not(is_one):
|
||||||
|
is_one = True
|
||||||
|
if sig_len >= mean_zero_len * 2:
|
||||||
|
morse += "\n"
|
||||||
|
if sig_len >= mean_zero_len:
|
||||||
|
morse += " "
|
||||||
|
|
||||||
|
sig_len = 0
|
||||||
|
|
||||||
|
if signal == 0 and is_one:
|
||||||
|
is_one = False
|
||||||
|
if sig_len > mean_one_len:
|
||||||
|
morse += "-"
|
||||||
|
else:
|
||||||
|
morse += "."
|
||||||
|
sig_len = 0
|
||||||
|
|
||||||
|
sig_len += 1
|
||||||
|
|
||||||
|
|
||||||
|
# extra space added at the end to access the
|
||||||
|
# last morse code
|
||||||
|
MORSE_CODE_DICT = { 'A':'.-', 'B':'-...',
|
||||||
|
'C':'-.-.', 'D':'-..', 'E':'.',
|
||||||
|
'F':'..-.', 'G':'--.', 'H':'....',
|
||||||
|
'I':'..', 'J':'.---', 'K':'-.-',
|
||||||
|
'L':'.-..', 'M':'--', 'N':'-.',
|
||||||
|
'O':'---', 'P':'.--.', 'Q':'--.-',
|
||||||
|
'R':'.-.', 'S':'...', 'T':'-',
|
||||||
|
'U':'..-', 'V':'...-', 'W':'.--',
|
||||||
|
'X':'-..-', 'Y':'-.--', 'Z':'--..',
|
||||||
|
'1':'.----', '2':'..---', '3':'...--',
|
||||||
|
'4':'....-', '5':'.....', '6':'-....',
|
||||||
|
'7':'--...', '8':'---..', '9':'----.',
|
||||||
|
'0':'-----', ', ':'--..--', '.':'.-.-.-',
|
||||||
|
'?':'..--..', '/':'-..-.', '-':'-....-',
|
||||||
|
'(':'-.--.', ')':'-.--.-'}
|
||||||
|
|
||||||
|
message = morse + ' '
|
||||||
|
|
||||||
|
decipher = ''
|
||||||
|
citext = ''
|
||||||
|
for letter in message:
|
||||||
|
if (not(letter in [' ', '\n'])):
|
||||||
|
citext += letter
|
||||||
|
else:
|
||||||
|
if (citext in MORSE_CODE_DICT.values()):
|
||||||
|
decipher += list(MORSE_CODE_DICT.keys())[list(MORSE_CODE_DICT.values()).index(citext)]
|
||||||
|
elif citext != '':
|
||||||
|
decipher += failChar
|
||||||
|
|
||||||
|
if (letter == '\n'):
|
||||||
|
decipher += ' '
|
||||||
|
|
||||||
|
citext = ''
|
||||||
|
|
||||||
|
f = open(args.output_file, "w+")
|
||||||
|
f.write(decipher)
|
||||||
|
f.close()
|
@ -146,6 +146,16 @@
|
|||||||
|
|
||||||
$spectogramPipe->commit();
|
$spectogramPipe->commit();
|
||||||
|
|
||||||
|
$cwPipe = new \DAL\processPipe();
|
||||||
|
$cwPipe->name->set("CW Morse");
|
||||||
|
$cwPipe->pipe->set([
|
||||||
|
"baseband_spectogram.py {baseband} {artefactDir}/spectogram.png -fs {fs} -fc {freq}",
|
||||||
|
"cw_morse.py {baseband} {artefactDir}/morse.txt -fs {fs} -fc \"[?]\"",
|
||||||
|
"cp {baseband} {artefactDir}/{freq}_{fs}.s8"
|
||||||
|
]);
|
||||||
|
|
||||||
|
$cwPipe->commit();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOAA 19
|
* NOAA 19
|
||||||
@ -346,7 +356,7 @@
|
|||||||
$maxvalierCW->modulation->set($cw);
|
$maxvalierCW->modulation->set($cw);
|
||||||
$maxvalierCW->antenna->set($yagi);
|
$maxvalierCW->antenna->set($yagi);
|
||||||
$maxvalierCW->priority->set(0);
|
$maxvalierCW->priority->set(0);
|
||||||
$maxvalierCW->processPipe->set($spectogramPipe);
|
$maxvalierCW->processPipe->set($cwPipe);
|
||||||
$maxvalierCW->commit();
|
$maxvalierCW->commit();
|
||||||
|
|
||||||
// add autoplas
|
// add autoplas
|
||||||
|
Loading…
x
Reference in New Issue
Block a user