OttoYu commited on
Commit
3541a66
·
verified ·
1 Parent(s): 8c3dd3c

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +275 -0
app.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import subprocess
3
+ import gzip
4
+ import shutil
5
+ import numpy as np
6
+ import matplotlib.pyplot as plt
7
+ import random
8
+ import plotly.graph_objects as go
9
+ from datetime import datetime, timedelta, timezone
10
+ import gradio as gr
11
+
12
+
13
+ def map_to_system(sat_num):
14
+ sat_num = int(sat_num)
15
+ if 1 <= sat_num <= 100:
16
+ return 'GPS'
17
+ elif 100 <= sat_num <= 199:
18
+ return 'GLONASS'
19
+ elif 201 <= sat_num <= 299:
20
+ return 'Galileo'
21
+ else:
22
+ return 'BeiDou'
23
+
24
+
25
+ def run_rinex2snr(station_code, year, day_of_year):
26
+ command = [
27
+ 'rinex2snr',
28
+ station_code,
29
+ year,
30
+ day_of_year,
31
+ '-nolook',
32
+ 'T',
33
+ '-snr', '88',
34
+ '-orb', 'gnss'
35
+ ]
36
+ try:
37
+ subprocess.run(command, check=True)
38
+ return True, "Command executed successfully."
39
+ except subprocess.CalledProcessError as e:
40
+ return False, f"Error executing command: {e}"
41
+
42
+
43
+ def unzip_file(gz_path):
44
+ txt_path = gz_path[:-3]
45
+ if os.path.exists(gz_path):
46
+ with gzip.open(gz_path, 'rb') as f_in, open(txt_path, 'wb') as f_out:
47
+ shutil.copyfileobj(f_in, f_out)
48
+ return True, txt_path
49
+ else:
50
+ return False, f"Output file {gz_path} not found."
51
+
52
+
53
+ def plot_polar(file_path):
54
+ data = np.loadtxt(file_path)
55
+ if data.shape[1] < 4:
56
+ raise ValueError('Data file must contain at least 4 columns: satellite number, elevation, azimuth, timestamps.')
57
+
58
+ satellite_numbers = data[:, 0]
59
+ elevation_angles = data[:, 1]
60
+ azimuth_angles = data[:, 2]
61
+ timestamps = data[:, 3]
62
+
63
+ valid_idx = (
64
+ ~np.isnan(satellite_numbers) &
65
+ ~np.isnan(elevation_angles) &
66
+ ~np.isnan(azimuth_angles) &
67
+ ~np.isnan(timestamps) &
68
+ (elevation_angles >= 0) & (elevation_angles <= 90) &
69
+ (azimuth_angles >= 0) & (azimuth_angles <= 360)
70
+ )
71
+
72
+ satellite_numbers = satellite_numbers[valid_idx]
73
+ elevation_angles = elevation_angles[valid_idx]
74
+ azimuth_angles = azimuth_angles[valid_idx]
75
+ timestamps = timestamps[valid_idx]
76
+
77
+ system_map = np.vectorize(
78
+ lambda sat: 'GPS' if 1 <= sat < 100 else
79
+ 'GLONASS' if 100 <= sat < 200 else
80
+ 'Galileo' if 200 <= sat < 300 else
81
+ 'BeiDou'
82
+ )
83
+
84
+ systems = system_map(satellite_numbers)
85
+ unique_systems = np.unique(systems)
86
+
87
+ satellite_colors = {sat: (random.random(), random.random(), random.random()) for sat in
88
+ np.unique(satellite_numbers)}
89
+
90
+ fig, axes = plt.subplots(2, 2, figsize=(12, 10), subplot_kw={'projection': 'polar'})
91
+ axes = axes.flatten()
92
+
93
+ radii = 90 - elevation_angles
94
+ thetas = np.deg2rad(azimuth_angles)
95
+
96
+ for i, system in enumerate(unique_systems):
97
+ ax = axes[i]
98
+ ax.set_ylim(0, 90)
99
+ ax.set_xticks(np.linspace(0, 2 * np.pi, 13)[:-1])
100
+ ax.set_xticklabels([f'{i}°' for i in range(0, 360, 30)], fontsize=10)
101
+ ax.set_yticks(range(0, 91, 10))
102
+ ax.set_yticklabels([f'{90 - i}°' for i in range(0, 91, 10)], fontsize=10)
103
+ ax.set_theta_zero_location('N')
104
+ ax.set_theta_direction(-1)
105
+ ax.set_title(f'{system} Satellite Trajectories', fontsize=12)
106
+ ax.grid(True, linestyle='--', alpha=0.7)
107
+ ax.axhline(90 - 15, color='red', linestyle='--', linewidth=1)
108
+
109
+ sys_mask = systems == system
110
+ sys_theta = thetas[sys_mask]
111
+ sys_radii = radii[sys_mask]
112
+ sys_timestamps = timestamps[sys_mask]
113
+
114
+ sort_idx = np.argsort(sys_timestamps)
115
+ sys_theta = sys_theta[sort_idx]
116
+ sys_radii = sys_radii[sort_idx]
117
+ sys_satellites = satellite_numbers[sys_mask][sort_idx]
118
+
119
+ unique_satellites, satellite_indices = np.unique(sys_satellites, return_inverse=True)
120
+ for sat in unique_satellites:
121
+ sat_mask = satellite_indices == np.where(unique_satellites == sat)[0][0]
122
+ if np.any(sat_mask):
123
+ ax.scatter(sys_theta[sat_mask], sys_radii[sat_mask],
124
+ s=10, c=[satellite_colors[sat]], label=f'Satellite {int(sat)}', alpha=0.6)
125
+
126
+ plt.tight_layout()
127
+ return fig # matplotlib figure
128
+
129
+
130
+ def plot_satellite_data(file_path):
131
+ data = np.loadtxt(file_path)
132
+ if data.shape[1] < 8:
133
+ raise ValueError('Data file must contain at least 8 columns.')
134
+
135
+ satellite_numbers = data[:, 0]
136
+ elevation_angles = data[:, 1]
137
+ timestamps = data[:, 3]
138
+ s1_snr = data[:, 6]
139
+ s2_snr = data[:, 7]
140
+
141
+ valid_idx = (
142
+ ~np.isnan(satellite_numbers) &
143
+ ~np.isnan(elevation_angles) &
144
+ ~np.isnan(timestamps) &
145
+ ~np.isnan(s1_snr) &
146
+ ~np.isnan(s2_snr)
147
+ )
148
+ satellite_numbers = satellite_numbers[valid_idx]
149
+ elevation_angles = elevation_angles[valid_idx]
150
+ timestamps = timestamps[valid_idx]
151
+ s1_snr = s1_snr[valid_idx]
152
+ s2_snr = s2_snr[valid_idx]
153
+
154
+ timestamps_utc8 = timestamps + 28800
155
+
156
+ bin_size = 900
157
+ bins = np.arange(np.min(timestamps_utc8), np.max(timestamps_utc8) + bin_size, bin_size)
158
+
159
+ num_bins = len(bins) - 1
160
+ elevation_mask = elevation_angles > 15
161
+ s1_counts = np.zeros(num_bins)
162
+ s2_counts = np.zeros(num_bins)
163
+ both_counts = np.zeros(num_bins)
164
+ total_counts = np.zeros(num_bins)
165
+ system_counts = {system: np.zeros(num_bins) for system in ['GPS', 'GLONASS', 'Galileo', 'BeiDou']}
166
+
167
+ system_map = np.array([map_to_system(sat) for sat in satellite_numbers])
168
+
169
+ for j in range(num_bins):
170
+ bin_mask = (timestamps_utc8 >= bins[j]) & (timestamps_utc8 < bins[j + 1])
171
+ valid_mask = bin_mask & elevation_mask
172
+
173
+ s1_counts[j] = len(np.unique(satellite_numbers[valid_mask & (s1_snr > 0.5)]))
174
+ s2_counts[j] = len(np.unique(satellite_numbers[valid_mask & (s2_snr > 0.5)]))
175
+ both_counts[j] = len(np.unique(satellite_numbers[valid_mask & (s1_snr > 0.5) & (s2_snr > 0.5)]))
176
+ total_counts[j] = len(np.unique(satellite_numbers[bin_mask]))
177
+
178
+ for system in system_counts.keys():
179
+ sys_mask = system_map == system
180
+ system_counts[system][j] = len(
181
+ np.unique(satellite_numbers[valid_mask & sys_mask & (s1_snr > 0.5) & (s2_snr > 0.5)]))
182
+
183
+ bin_datetimes = [datetime.fromtimestamp(t, tz=timezone(timedelta(hours=8))) for t in bins]
184
+
185
+ # Plot 1
186
+ fig1 = go.Figure()
187
+ fig1.add_trace(
188
+ go.Scatter(x=bin_datetimes[:-1], y=s1_counts, mode='lines', name='L1 Satellites', line=dict(color='blue')))
189
+ fig1.add_trace(
190
+ go.Scatter(x=bin_datetimes[:-1], y=s2_counts, mode='lines', name='L2 Satellites', line=dict(color='green')))
191
+ fig1.add_trace(go.Scatter(x=bin_datetimes[:-1], y=both_counts, mode='lines', name='L1 and L2 Satellites',
192
+ line=dict(color='orange')))
193
+ fig1.update_layout(title='Number of Satellites in L1, L2, and Both L1 and L2 Over Time',
194
+ xaxis_title='Time (UTC+8)', yaxis_title='Number of Satellites',
195
+ xaxis_tickformat='%H:%M', template='plotly_white', height=300, margin=dict(t=40))
196
+
197
+ # Plot 2
198
+ fig2 = go.Figure()
199
+ for system in system_counts.keys():
200
+ fig2.add_trace(go.Scatter(x=bin_datetimes[:-1], y=system_counts[system], mode='lines', name=system))
201
+ fig2.update_layout(title='Number of Satellites for Linear Combination by System Over Time',
202
+ xaxis_title='Time (UTC+8)', yaxis_title='Number of Satellites',
203
+ xaxis_tickformat='%H:%M', template='plotly_white', height=300, margin=dict(t=40))
204
+
205
+ # Plot 3
206
+ fig3 = go.Figure()
207
+ fig3.add_trace(go.Scatter(x=bin_datetimes[:-1], y=total_counts, mode='lines', name='Total Satellites',
208
+ line=dict(color='black', dash='dash')))
209
+ for system in system_counts.keys():
210
+ counts_without_filter = np.zeros(num_bins)
211
+ for j in range(num_bins):
212
+ bin_mask = (timestamps_utc8 >= bins[j]) & (timestamps_utc8 < bins[j + 1])
213
+ sys_mask = system_map == system
214
+ counts_without_filter[j] = len(np.unique(satellite_numbers[bin_mask & sys_mask]))
215
+ fig3.add_trace(go.Scatter(x=bin_datetimes[:-1], y=counts_without_filter, mode='lines', name=system))
216
+ fig3.update_layout(title='Total Satellite Observations and System Counts Over Time',
217
+ xaxis_title='Time (UTC+8)', yaxis_title='Number of Satellites',
218
+ xaxis_tickformat='%H:%M', template='plotly_white', height=300, margin=dict(t=40))
219
+
220
+ return fig1, fig2, fig3
221
+
222
+
223
+ def process_file_and_plot(uploaded_file):
224
+ # Extract info from filename
225
+ filename = os.path.basename(uploaded_file.name)
226
+ station_code = filename[:4]
227
+ day_of_year = filename[4:7]
228
+ year = f"20{filename[9:11]}"
229
+
230
+ # Run rinex2snr subprocess
231
+ success, msg = run_rinex2snr(station_code, year, day_of_year)
232
+ if not success:
233
+ return f"Subprocess failed: {msg}", None, None, None, None
234
+
235
+ # Path to gz file
236
+ gz_file = f"./{year}/snr/{station_code}/{station_code}{day_of_year}0.{year[2:]}.snr88.gz"
237
+ # Unzip file
238
+ success, result = unzip_file(gz_file)
239
+ if not success:
240
+ return f"Unzip failed: {result}", None, None, None, None
241
+
242
+ txt_file = result
243
+
244
+ # Generate plots
245
+ try:
246
+ polar_fig = plot_polar(txt_file)
247
+ fig1, fig2, fig3 = plot_satellite_data(txt_file)
248
+ except Exception as e:
249
+ return f"Plotting error: {e}", None, None, None, None
250
+
251
+ return "Success!", polar_fig, fig1, fig2, fig3
252
+
253
+
254
+ with gr.Blocks() as demo:
255
+ gr.Markdown("## RINEX Satellite Data Processing and Visualization", elem_id="title")
256
+
257
+ file_input = gr.File(label="Upload RINEX observation file (.xxo)")
258
+ status = gr.Textbox(value="", interactive=False, label="Status")
259
+
260
+ with gr.Row():
261
+ with gr.Column(scale=1):
262
+ polar_plot = gr.Plot(label="Polar Plot (matplotlib)", elem_id="polar_plot_container")
263
+ with gr.Column(scale=1):
264
+ line1 = gr.Plot(label="L1, L2, Both Satellites Over Time", elem_classes="line_plot")
265
+ line2 = gr.Plot(label="Satellites by System (with Filters)", elem_classes="line_plot")
266
+ line3 = gr.Plot(label="Total Satellites and System Counts", elem_classes="line_plot")
267
+
268
+ file_input.change(
269
+ fn=process_file_and_plot,
270
+ inputs=[file_input],
271
+ outputs=[status, polar_plot, line1, line2, line3],
272
+ show_progress=True
273
+ )
274
+
275
+ demo.launch()