Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 27 additions & 12 deletions H-line.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import contextlib
import os, sys, json
from time import sleep
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone

# Import necessary classes/modules
sys.path.append("src/")
Expand All @@ -21,8 +21,11 @@ def main(config):

sdr = Observation.getSDR(**SDR_PARAM)

# live_view takes priority
live_view = PLOTTING_PARAM['live_view']

# If user wants 24h observations
if OBSERVATION_PARAM["24h"]:
if not live_view and OBSERVATION_PARAM["24h"]:
# Checks if 360 is divisable with the degree interval and calculates number of collections
try:
deg_interval = OBSERVATION_PARAM["degree_interval"]
Expand All @@ -33,11 +36,17 @@ def main(config):
quit()
else:
num_data = 1
if live_view:
num_data = 100

# Do observation(s)
# Start time of observation
current_time = datetime.utcnow()
current_time = datetime.now(timezone.utc)
for i in range(num_data):
if 0 == i or not live_view:
sdr.set_bias_tee(SDR_PARAM['bias_tee'])
# sleep to allow the amplifier to settle 1ms should be enough time
sleep(1)
COORD_CLASS = Observation.getCoordinates(current_time + timedelta(seconds = 24*60**2/num_data * i), **OBSERVER_PARAM)
print(current_time + timedelta(seconds = 24*60**2/num_data * i))

Expand All @@ -49,12 +58,15 @@ def main(config):
print("Analyzing data...")
Observation.analyzeData(COORD_CLASS)
print("Plotting data...")
Observation.plotData(**PLOTTING_PARAM)

print(f"Done observing! - {datetime.utcnow()}")
PLOTTING_PARAM['n_plot'] = i
if Observation.plotData(**PLOTTING_PARAM):
print(f"Live view finished!")
break
else:
print(f"Done observing! - {datetime.now(timezone.utc)}")

# Next, write datafile if necessary
if OBSERVATION_PARAM["datafile"]:
if not live_view and OBSERVATION_PARAM["datafile"]:
user_params = {
"SDR": SDR_PARAM,
"DSP": DSP_PARAM,
Expand All @@ -64,13 +76,17 @@ def main(config):
Observation.writeDatafile(**user_params)

# Wait for next execution
if num_data > 1:
if not live_view and num_data > 1:
end_time = current_time + timedelta(seconds = second_interval * (i + 1))
time_remaining = end_time - datetime.utcnow()
print(f'Waiting for next data collection in {time_remaining.total_seconds()} seconds')
sleep(time_remaining.total_seconds())
time_remaining = end_time - datetime.now(timezone.utc)
delay = time_remaining.total_seconds()
delay = 1 if delay < 0 else delay
print(f'Waiting for next data collection in {delay} seconds')
sdr.set_bias_tee(False)
sleep(delay)
clear_console()

sdr.set_bias_tee(False)

# Reads user config
def read_config():
Expand All @@ -79,7 +95,6 @@ def read_config():
parsed_config = json.load(config)
main(parsed_config)


# For clearing console
def clear_console():
os.system('cls' if os.name =='nt' else 'clear')
Expand Down
6 changes: 4 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"PPM_offset": 0,
"TCP_host": false,
"connect_to_host": false,
"host_IP": "127.0.0.1"
"host_IP": "127.0.0.1",
"bias_tee": true
},
"DSP": {
"number_of_fft": 1000,
Expand All @@ -21,7 +22,8 @@
"plotting": {
"plot_map": true,
"y_min": 0.0,
"y_max": 0.0
"y_max": 0.0,
"live_view": true
},
"observation": {
"24h": false,
Expand Down
10 changes: 6 additions & 4 deletions src/observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ def analyzeData(self, coord_class):

# Plot the data
def plotData(self, **params):
PLOT = Plotter(params["plot_map"], params["y_min"], params["y_max"])
live_view = False
if 'live_view' in params:
live_view = params['live_view']
PLOT = Plotter(params["plot_map"], params["y_min"], params["y_max"], live_view)

plot_info = {
"ra": self.RA,
Expand All @@ -96,8 +99,7 @@ def plotData(self, **params):
"SNR": self.max_SNR,
"observed_radial_velocity": self.observed_radial_velocity
}
PLOT.plot(self.freqs,self.SNR_spectrum,**plot_info)

return PLOT.plot(self.freqs,self.SNR_spectrum,**plot_info)

# Writes a datafile with all the collected data from the observation
def writeDatafile(self, **kwargs):
Expand Down Expand Up @@ -130,4 +132,4 @@ def writeDatafile(self, **kwargs):





46 changes: 30 additions & 16 deletions src/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
ANALYSIS = Analysis()

class Plotter():
def __init__(self, plot_map, y_min, y_max):
def __init__(self, plot_map, y_min, y_max, live_view = False):
self.SHOW_MAP = plot_map
self.Y_MIN = y_min
self.Y_MAX = y_max
self.live_view = live_view

def plot(self, freqs, data, **kwargs):
# Unpack info
Expand All @@ -21,7 +22,8 @@ def plot(self, freqs, data, **kwargs):
freq_correction = ANALYSIS.freqFromRadialVel(barycenter_correction + lsr_correction) - ANALYSIS.H_FREQUENCY
SNR, radial_velocity = kwargs["SNR"], kwargs["observed_radial_velocity"]

if self.SHOW_MAP:
user_closed_window = False
if not self.live_view and self.SHOW_MAP:
fig = plt.figure(figsize=(20,12))
fig.suptitle('Hydrogen line observation', fontsize = 22, y = 0.99)
fig.subplots_adjust(hspace=1)
Expand All @@ -41,18 +43,30 @@ def plot(self, freqs, data, **kwargs):
corrected_spectrum_ax.set_yticklabels([])
corrected_spectrum_ax.set_ylabel('')

else:
fig, ax = plt.subplots(figsize = (12, 7))
elif self.live_view:
if plt.fignum_exists(1):
fig = plt.figure(1)
ax = fig.axes[0]
plt.cla()
else:
fig, ax = plt.subplots(figsize = (12, 7))

self.spectrumGrid(ax, "Observed spectrum", freqs, data)


# Saves plot
path = f'./Spectrums/ra={ra},dec={dec}.png'
plt.tight_layout(pad = 1.75)
plt.savefig(path, dpi = 100)
plt.close()
if self.live_view:
plt.pause(1.0)
if len(plt.get_fignums()) == 0:
user_closed_window = True
else:
# Saves plot
path = f'./Spectrums/ra={ra},dec={dec}.png'
plt.tight_layout(pad = 1.75)
plt.savefig(path, dpi = 100)

plt.close()

return user_closed_window


# Arrange detail grid
# TODO: Redesign table. Perhaps into two subplots
def detailsGrid(self, ax, ra, dec,gal_lon, gal_lat, barycenter_correction, lsr_correction, radial_velocity, SNR):
Expand All @@ -78,8 +92,8 @@ def detailsGrid(self, ax, ra, dec,gal_lon, gal_lat, barycenter_correction, lsr_c
table.auto_set_font_size(False)
table.set_fontsize(14)
table.scale(1, 2.25)


# Arrange sky grid
def skyGrid(self, ax, ra, dec):
ax.set(title = 'Milky Way H-line map')
Expand Down Expand Up @@ -108,7 +122,7 @@ def spectrumGrid(self, ax, title, freqs, data):

# Plots theoretical H-line frequency
ax.axvline(x = ANALYSIS.H_FREQUENCY, color = 'r', linestyle = ':', linewidth = 2, label = 'Theoretical frequency')

# Sets axis labels and adds legend & grid
ylabel ='Signal to noise ratio (SNR) / dB'
xlabel = 'Frequency / Hz'
Expand All @@ -128,11 +142,11 @@ def spectrumGrid(self, ax, title, freqs, data):
# Adds top x-axis for radial velocity
radial_vel = ax.secondary_xaxis('top', functions = (ANALYSIS.radialVelFromFreq, ANALYSIS.freqFromRadialVel))
radial_vel.set_xlabel(r'Radial velocity / $\frac{km}{s}$')


# Generates and saves a GIF of 24H observations
def generateGIF(self, ra, dec):
print('Generating GIF from observations... This may take a while')
path = f'Spectrums/ra={ra[0]},dec={dec}.gif'
images = [imageio.imread(f'Spectrums/ra={coord},dec={dec}.png') for coord in ra]
imageio.mimsave(path, images)
imageio.mimsave(path, images)
37 changes: 32 additions & 5 deletions src/ui/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
SAMPLE_RATES = [3200000,2800000,2560000,2400000,2048000,1920000,1800000,1400000,1024000,900001,250000]

# Define default parameters
parameters = {
_parameters = {
"SDR": {
"sample_rate": 2400000,
"PPM_offset": 0,
"TCP_host": False,
"connect_to_host": False,
"host_IP": "127.0.0.1"
"host_IP": "127.0.0.1",
"bias_tee": False
},
"DSP": {
"number_of_fft": 1000,
Expand All @@ -30,7 +31,8 @@
"plotting":{
"plot_map": True,
"y_min": 0.0,
"y_max": 0.0
"y_max": 0.0,
"live_view": False
},
"observation": {
"24h": False,
Expand All @@ -39,6 +41,26 @@
}
}

parameters = {}

# Initial with either config.json or _parameters
def load_defaults():
if len(parameters):
return

from pathlib import Path
config = Path("config.json")
if config.is_file():
read_from_config()

# now add any new default parameters
for category in _parameters.keys():
for key, value in _parameters[category].items():
if not category in parameters:
parameters[category] = {}

if not key in parameters[category]:
parameters[category][key] = value

# Write parameters to config
def update_config():
Expand All @@ -50,11 +72,13 @@ def read_from_config():
with open("config.json", "r") as config_file:
parsed_config = json.load(config_file)
categories = list(parsed_config.keys())
# Iterrate over each key in category (SDR, DSP, etc.)

# Iterate over each key in category (SDR, DSP, etc.)
for category in categories:
for key, value in parsed_config[category].items():
dpg.set_value(key, value)
if not category in parameters:
parameters[category] = {}
parameters[category][key] = value

# Callback functions
Expand Down Expand Up @@ -84,6 +108,9 @@ def btn_callback(sender, app_data, user_data):
# Handle checkbox actions
def checkbox_callback(sender, app_data, user_data):
parameters[user_data][sender] = app_data
if sender == 'live_view':
update_config()
if app_data: os.system('py H-line.py' if os.name =='nt' else 'python3 H-line.py')

# Handle text actions
def text_callback(sender, app_data, user_data):
Expand Down
13 changes: 11 additions & 2 deletions src/ui/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ def sdrWindow():
dpg.add_text("RTL-SDR parameters")
dpg.add_text("(help)",tag="RTL-SDR_category")

dpg.add_checkbox(label="bias tee",tag="bias_tee",user_data="SDR",default_value=False,callback=callbacks.checkbox_callback)
dpg.add_combo(callbacks.SAMPLE_RATES,label="Sample rate",user_data="SDR",default_value=2400000,tag="sample_rate",callback=callbacks.dropdown_callback)
dpg.add_input_int(label="Resolution",user_data="SDR",default_value=11,tag="resolution",callback=callbacks.text_callback)
dpg.add_input_int(label="PPM offset",user_data="SDR",default_value=0,tag="PPM_offset",callback=callbacks.text_callback)
dpg.add_spacer(height=5)

with dpg.tooltip("RTL-SDR_category"):
help_msg = '''
bias tee = Configure bias tee (configures voltage on RTL-SDR input port)
Sample rate = Sample rate of SDR. Values above 2.4MSPS may cause sample drops.
Resolution = 2^Resolution samples are collected pr. FFT.
PPM offset = Offset of the RTL-SDR local oscillator in parts per million.
Expand Down Expand Up @@ -134,17 +136,24 @@ def actionsWindow():
with dpg.group(horizontal=True):
dpg.add_text("Perform actions")
dpg.add_text("(help)", tag="action_category")
# dpg.add_button(label="Live view",tag="live_view",callback=callbacks.btn_callback)

dpg.add_checkbox(label="Live view",tag="live_view",user_data="plotting",default_value=False,callback=callbacks.checkbox_callback)
dpg.add_button(label="Edit theme",tag="edit_theme",callback=callbacks.btn_callback)
dpg.add_button(label="Browse observations",tag="open_obs_folder",callback=callbacks.btn_callback)
dpg.add_button(label="Update parameters from config",tag="update_parameters",callback=callbacks.btn_callback)

with dpg.tooltip("action_category"):
# Live view = Open a live spectrum for locating the H-line.
help_msg = '''
Live view = Open a live spectrum for locating the H-line.
Edit theme = Edit the look and appearance of the user interface.
Browse observations = Browse the folder with previous observations.
Update parameters from config = Updates the UI with the parameters from the config file.
'''
dpg.add_text(textwrap.dedent(help_msg))

def readDefaults():
# Once the windows have been created populate with default values
callbacks.load_defaults()
callbacks.update_config()
callbacks.read_from_config()
3 changes: 2 additions & 1 deletion ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def run_ui():
parameters.observerWindow()
parameters.observationWindow()
parameters.actionsWindow()
parameters.readDefaults()

dpg.setup_dearpygui()
dpg.show_viewport()
Expand All @@ -23,4 +24,4 @@ def run_ui():

if __name__ == "__main__":
print("Launching user interface!")
run_ui()
run_ui()