-
Notifications
You must be signed in to change notification settings - Fork 1
/
track.cpp
302 lines (281 loc) · 9.7 KB
/
track.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <queue>
#include <string>
#include "DateTime.hpp"
#include "meb_print.h"
#include "CoordTopocentric.hpp"
#include "SGP4.hpp"
#include "Observer.hpp"
#define SEC *1000000 // nanoseconds to seconds
#define DEG *(180/3.1415926) // radians to degrees
#define GS_LAT 42.655583
#define GS_LON -71.325433
#define ELEV 0.061 // Lowell ASL + Olney Height; Kilometers for some reason.
#define MIN_ELEV 10.0 // degrees
#define ELEV_ADJ 0 // degrees adjustment +-
#define AZIM_ADJ -34 // degrees adjustment +-
using namespace LSGP4;
volatile sig_atomic_t done = 0;
void sighandler(int sig)
{
done = 1;
}
char cmdbuf[512];
char fname[128];
std::queue<std::string> files;
pthread_mutex_t queue = PTHREAD_MUTEX_INITIALIZER;
void *compress_cleanup(void *in)
{
while (!done)
{
char cmd[512];
std::string fn = "";
pthread_mutex_lock(&queue);
if (!files.empty())
fn = files.front();
pthread_mutex_unlock(&queue);
if (fn != "")
{
snprintf(cmd, sizeof(cmd), "7za a -mm=BZip2 %s.bzip2 %s.bin", fn.c_str(), fn.c_str());
system(cmd);
snprintf(cmd, sizeof(cmd), "rm -f %s.bin", fn.c_str());
system(cmd);
pthread_mutex_lock(&queue);
files.pop();
pthread_mutex_unlock(&queue);
}
sleep(1);
}
return NULL;
}
void *datacollect(void *in)
{
system(cmdbuf);
pthread_mutex_lock(&queue);
files.push(std::string(fname));
pthread_mutex_unlock(&queue);
return NULL;
}
static char *get_time_now_raw()
{
static __thread char buf[128];
time_t t = time(NULL);
struct tm tm = *localtime(&t);
snprintf(buf, sizeof(buf), "%02d%02d%02d",
tm.tm_hour, tm.tm_min, tm.tm_sec);
return buf;
}
static char *get_datetime_now_raw()
{
static __thread char buf[128];
time_t t = time(NULL);
struct tm tm = *localtime(&t);
snprintf(buf, sizeof(buf), "%04d%02d%2d_%02d%02d%02d",
tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
return buf;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Invocation: %s <File containing TLE Set>\n\n", argv[0]);
return 0;
}
// read TLEs
auto objs = ReadTleFromFile(argv[1]);
bprintlf(BLUE_FG "Read %d TLEs", objs.size());
if (objs.size() < 1)
{
dbprintlf(FATAL "Did not read any valid TLEs, exiting");
return 0;
}
// set up signal handler
signal(SIGINT, sighandler);
// set up targets
std::vector<SGP4> targets(objs.size(), objs[0]); // no default constructor exists, so create vector with all same TLEs
for (int i = 0; i < objs.size(); i++)
{
targets[i].SetTle(Tle(objs[i])); // set individual TLEs
}
// set up observer
Observer *dish = new Observer(GS_LAT, GS_LON, ELEV);
// get into main loop
bool pending_az = false;
bool pending_el = false;
bool pending_any = false;
bool sat_viewable = false;
double cmd_az = 0;
double cmd_el = M_PI_2;
int sleep_timer = 0;
int sleep_timer_max = 0;
int pass_length = 0;
pthread_t jobthread = 0;
pthread_t compressthread = 0;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (pthread_create(&compressthread, NULL, &compress_cleanup, NULL))
{
errprintlf(FATAL "Could not create compress thread");
}
SGP4 *current_target = nullptr;
// Set up for network updating TLEs.
const char *url = "https://celestrak.com/NORAD/elements/stations.txt";
bool initial_load = true;
DateTime launch_time;
launch_time = launch_time.Now();
while (!done) // main loop
{
// Step 1: Execute command
sleep(1);
// should we be sleeping?
if (sleep_timer)
{
if (sleep_timer > sleep_timer_max) // update max
sleep_timer_max = sleep_timer;
if (sleep_timer == 21 && current_target != nullptr) // 21 second mark, now we prepare command
{
snprintf(fname, sizeof(fname), "%s_%d_%d", get_datetime_now_raw(), pass_length, current_target->GetTle().NoradNumber());
snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/python3 rtl_sdr_iqcapture_250k.py %d %s.bin", pass_length + 40, fname);
}
if (sleep_timer == 20 && current_target != nullptr) // 20 seconds mark, now we command
{
if (jobthread != 0)
pthread_join(jobthread, NULL);
if (pthread_create(&jobthread, &attr, &datacollect, NULL))
errprintlf("Error creating data collect thread");
}
sleep_timer--;
tprintlf(BLUE_FG "Will be sleeping for %d more seconds...", sleep_timer);
continue;
}
else
{
sleep_timer_max = 0;
}
// Step 1: Get time now
DateTime tnow = DateTime::Now(true);
// for each object figure out current position
CoordTopocentric current_pos;
for (int i = 0; i < targets.size(); i++)
{
SGP4 *target = &targets[i];
Eci pos_now = target->FindPosition(tnow);
current_pos = dish->GetLookAngle(pos_now);
CoordGeodetic current_lla = pos_now.ToGeodetic();
tprintlf("Obj %d: %.2f AZ, %.2f EL | %.2f LA, %.2f LN", target->GetTle().NoradNumber(), current_pos.azimuth DEG, current_pos.elevation DEG, current_lla.latitude DEG, current_lla.longitude DEG);
if (current_pos.elevation DEG > MIN_ELEV && current_target == nullptr) // we are already in a pass, scramble!
{
current_target = target;
break;
}
}
// Step 2: Figure out which object we will see in the next 4 minutes if we are not already in a pass
DateTime tnext = tnow;
#define LOOKAHEAD_MIN 1
#define LOOKAHEAD_MAX 2
tnext = tnext.AddMinutes(LOOKAHEAD_MAX); // 4 minutes lookahead
for (int j = 0; j < targets.size() && current_target == nullptr; j++)
{
SGP4 *target = &targets[j];
if (launch_time.AddDays(1) < tnow || initial_load)
{
objs[j].UpdateFromNetwork(url);
target->SetTle(objs[j]);
launch_time = tnow;
}
for (int i = 0; i < (LOOKAHEAD_MAX - LOOKAHEAD_MIN) * 60; i++)
{
Eci eci_ahd = target->FindPosition(tnext);
CoordTopocentric pos_ahd = dish->GetLookAngle(eci_ahd);
if (i == 0)
tprintlf(GREEN_BG RED_FG "Lookahead %d: %.2f AZ %.2f EL", target->GetTle().NoradNumber(), pos_ahd.azimuth DEG, pos_ahd.elevation DEG);
int ahd_el = pos_ahd.elevation DEG;
if (ahd_el < (int)MIN_ELEV) // still not in view 4 minutes ahead, don't care
{
break;
}
if (ahd_el > (int)MIN_ELEV) // already up, find where it is at proper elevation
{
tnext = tnext.AddSeconds(-1);
}
else // right point
{
current_target = target;
cmd_az = pos_ahd.azimuth DEG;
cmd_el = pos_ahd.elevation DEG;
pending_az = true;
pending_el = true;
sleep_timer = LOOKAHEAD_MAX * 60 - i; // lookahead left
// check when the pass is over
pass_length = 0;
for (int el = ahd_el; el >= MIN_ELEV;)
{
pass_length++;
tnext = tnext.AddSeconds(1);
eci_ahd = target->FindPosition(tnext);
pos_ahd = dish->GetLookAngle(eci_ahd);
el = pos_ahd.elevation DEG;
}
// here we know the pass length
break; // break inner for loop
}
}
if (current_target != nullptr) // target has been assigned
break;
}
if (initial_load)
initial_load = false;
// Step 3: Are we in a pass?
if (current_target != nullptr)
{
Eci pos_now = current_target->FindPosition(tnow);
current_pos = dish->GetLookAngle(pos_now);
}
else
{
continue;
}
if (current_pos.elevation DEG > MIN_ELEV)
{
if (!sat_viewable) // satellite just became visible
{
}
sat_viewable = true;
if (fabs(cmd_az DEG - current_pos.azimuth DEG) > 1) // azimuth has changed
{
cmd_az = current_pos.azimuth DEG;
pending_az = true;
}
if (fabs(cmd_el DEG - current_pos.elevation DEG) > 1)
{
cmd_el = current_pos.elevation DEG;
pending_el = true;
}
continue;
}
// Step 4: Were we in a pass?
if (sat_viewable) // we are here, but sat_viewable is on. Meaning we just got out of a pass
{
sat_viewable = false;
cmd_az = -AZIM_ADJ;
cmd_el = 90;
pending_az = true;
pending_el = true;
sleep_timer = 120; // 120 seconds
current_target = nullptr;
}
sat_viewable = false;
}
// clean up
pthread_attr_destroy(&attr);
pthread_join(compressthread, NULL);
delete dish;
return 0;
}