1
0
Fork 0

Merge pull request #289 from satnogs/dev

Merge for 0.8.1
merge-requests/385/head 0.8.1
Nikos Roussos 2017-01-28 16:16:40 +02:00 committed by GitHub
commit 65b48e1375
16 changed files with 257 additions and 145 deletions

View File

@ -17,7 +17,7 @@ class DataSerializer(serializers.ModelSerializer):
class Meta:
model = Data
fields = ('id', 'start', 'end', 'observation', 'ground_station', 'transmitter',
'norad_cat_id', 'payload', 'demoddata')
'norad_cat_id', 'payload', 'waterfall', 'demoddata')
read_only_fields = ['id', 'start', 'end', 'observation', 'ground_station',
'transmitter', 'norad_cat_id']

View File

@ -14,3 +14,6 @@ class StationForm(forms.ModelForm):
class SatelliteFilterForm(forms.Form):
norad = forms.IntegerField()
start_date = forms.CharField(required=False)
end_date = forms.CharField(required=False)
ground_station = forms.IntegerField(required=False)

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-01-26 17:24
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0012_auto_20160508_1516'),
]
operations = [
migrations.AddField(
model_name='data',
name='waterfall',
field=models.ImageField(blank=True, null=True, upload_to=b'data_waterfalls'),
),
]

View File

@ -255,6 +255,7 @@ class Data(models.Model):
observation = models.ForeignKey(Observation)
ground_station = models.ForeignKey(Station)
payload = models.FileField(upload_to='data_payloads', blank=True, null=True)
waterfall = models.ImageField(upload_to='data_waterfalls', blank=True, null=True)
vetted_datetime = models.DateTimeField(null=True, blank=True)
vetted_user = models.ForeignKey(User, related_name="vetted_user_set", null=True, blank=True)
vetted_status = models.CharField(choices=OBSERVATION_STATUSES,

View File

@ -28,3 +28,11 @@ def percentagerest(value):
return 100 - value
except (TypeError, ValueError):
return 0
@register.filter
def truncatesecs(value):
try:
return value[:-3]
except (TypeError, ValueError):
return value

View File

@ -15,6 +15,9 @@ base_urlpatterns = ([
url(r'^observations/(?P<id>[0-9]+)/delete/$', views.observation_delete,
name='observation_delete'),
url(r'^observations/new/$', views.observation_new, name='observation_new'),
url(r'^prediction_windows/(?P<sat_id>[\w.@+-]+)/(?P<start_date>.+)/'
'(?P<end_date>.+)/(?P<station_id>[\w.@+-]+)/$',
views.prediction_windows, name='prediction_windows_filtered'),
url(r'^prediction_windows/(?P<sat_id>[\w.@+-]+)/(?P<start_date>.+)/(?P<end_date>.+)/$',
views.prediction_windows, name='prediction_windows'),
url(r'^data_verify/(?P<id>[0-9]+)/$', views.data_verify, name='data_verify'),

View File

@ -196,21 +196,31 @@ def observation_new(request):
satellites = Satellite.objects.filter(transmitters__alive=True).distinct()
transmitters = Transmitter.objects.filter(alive=True)
norad = 0
obs_filter = {}
if request.method == 'GET':
form = SatelliteFilterForm(request.GET)
if form.is_valid():
norad = form.cleaned_data['norad']
filter_form = SatelliteFilterForm(request.GET)
if filter_form.is_valid():
start_date = datetime.strptime(filter_form.cleaned_data['start_date'],
'%Y/%m/%d %H:%M').strftime('%Y-%m-%d %H:%M')
end_date = (datetime.strptime(filter_form.cleaned_data['end_date'], '%Y/%m/%d %H:%M') +
timedelta(minutes=1)).strftime('%Y-%m-%d %H:%M')
obs_filter['exists'] = True
obs_filter['norad'] = filter_form.cleaned_data['norad']
obs_filter['start_date'] = start_date
obs_filter['end_date'] = end_date
obs_filter['ground_station'] = filter_form.cleaned_data['ground_station']
else:
obs_filter['exists'] = False
return render(request, 'base/observation_new.html',
{'satellites': satellites,
'transmitters': transmitters, 'norad': norad,
'transmitters': transmitters, 'obs_filter': obs_filter,
'date_min_start': settings.DATE_MIN_START,
'date_min_end': settings.DATE_MIN_END,
'date_max_range': settings.DATE_MAX_RANGE})
def prediction_windows(request, sat_id, start_date, end_date):
def prediction_windows(request, sat_id, start_date, end_date, station_id=None):
try:
sat = Satellite.objects.filter(transmitters__alive=True). \
distinct().get(norad_cat_id=sat_id)
@ -237,6 +247,8 @@ def prediction_windows(request, sat_id, start_date, end_date):
data = []
stations = Station.objects.all()
if station_id:
stations = stations.filter(id=station_id)
for station in stations:
if not station.online:
continue
@ -437,36 +449,33 @@ def station_view(request, id):
if tr is None:
break
# bug in pyephem causes overhead sats to appear in the result
# mixing next-pass data with current pass data, resulting in
# satnogs/satnogs-network#199. As a workaround, pyephem does
# return set time for current pass while rise time for next
# pass so when this happens we want to toss the entry out
# not a break as this sat might have another valid pass
if ts < tr:
pass
# using the angles module convert the sexagesimal degree into
# something more easily read by a human
elevation = format(math.degrees(altt), '.0f')
azimuth = format(math.degrees(azr), '.0f')
passid += 1
# show only if >= configured horizon and in next 6 hours
# show only if >= configured horizon and in next 6 hours,
# and not directly overhead (tr < ts see issue 199)
if tr < ephem.date(datetime.today() + timedelta(hours=6)):
if float(elevation) >= station.horizon:
if (float(elevation) >= station.horizon and tr < ts):
valid = True
if tr < ephem.Date(datetime.now() +
timedelta(minutes=int(settings.DATE_MIN_START))):
valid = False
sat_pass = {'passid': passid,
'mytime': str(observer.date),
'debug': observer.next_pass(sat_ephem),
'name': str(satellite.name),
'id': str(satellite.id),
'norad_cat_id': str(satellite.norad_cat_id),
'tr': tr, # Rise time
'tr': str(tr), # Rise time
'azr': azimuth, # Rise Azimuth
'tt': tt, # Max altitude time
'altt': elevation, # Max altitude
'ts': ts, # Set time
'azs': azs} # Set azimuth
'ts': str(ts), # Set time
'azs': azs, # Set azimuth
'valid': valid}
nextpasses.append(sat_pass)
observer.date = ephem.Date(ts).datetime() + timedelta(minutes=1)
continue

View File

@ -354,6 +354,15 @@ code.log p {
float: right;
}
.waterfall {
margin-top: 5px;
}
.waterfall img {
margin: auto;
padding: 5px;
}
#collapseFilters {
padding: 15px;
}

View File

@ -5,7 +5,7 @@
* valid. -cshields
*/
function gridsquare() {
var FIELD_IDENTIFIERS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'H', 'K', 'L', 'M',
var FIELD_IDENTIFIERS = ['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'];
// starting points, the fields from the Station form

View File

@ -1,33 +1,41 @@
$( document ).ready( function(){
var minstart = $('#datetimepicker-start').data('date-minstart');
var minend = $('#datetimepicker-end').data('date-minend');
var maxrange = $('#datetimepicker-end').data('date-maxrange');
$('#datetimepicker-start').datetimepicker();
$('#datetimepicker-start').data('DateTimePicker').minDate(moment.utc().add(minstart, 'm'));
$('#datetimepicker-end').datetimepicker();
$('#datetimepicker-end').data('DateTimePicker').minDate(moment.utc().add(minend, 'm'));
$("#datetimepicker-start").on('dp.change',function (e) {
// Setting default, minimum and maximum for end
$('#datetimepicker-end').data('DateTimePicker').defaultDate(moment.utc(e.date).add(60, 'm'));
$('#datetimepicker-end').data('DateTimePicker').minDate(e.date);
$('#datetimepicker-end').data('DateTimePicker').maxDate(moment.utc(e.date).add(maxrange, 'm'));
});
function select_proper_transmitters(norad) {
$(document).ready( function(){
function select_proper_transmitters(satellite) {
$('#transmitter-selection').prop('disabled', false);
$('#transmitter-selection option').hide();
$('#transmitter-selection option[data-satellite="' + norad + '"]').show().prop('selected', true);
$('#transmitter-selection option[data-satellite="' + satellite + '"]').show().prop('selected', true);
$('.tle').hide();
$('.tle[data-norad="' + norad + '"]').show();
$('.tle[data-norad="' + satellite + '"]').show();
}
var norad = $(this).find(':selected').data('norad');
select_proper_transmitters(norad);
var satellite;
var obs_filter = $('#form-obs').data('obs-filter');
if (obs_filter) {
satellite = $('input[name="satellite"]').val();
ground_station = $('input[name="ground_station"]').val();
} else {
var minstart = $('#datetimepicker-start').data('date-minstart');
var minend = $('#datetimepicker-end').data('date-minend');
var maxrange = $('#datetimepicker-end').data('date-maxrange');
$('#datetimepicker-start').datetimepicker();
$('#datetimepicker-start').data('DateTimePicker').minDate(moment.utc().add(minstart, 'm'));
$('#datetimepicker-end').datetimepicker();
$('#datetimepicker-end').data('DateTimePicker').minDate(moment.utc().add(minend, 'm'));
$("#datetimepicker-start").on('dp.change',function (e) {
// Setting default, minimum and maximum for end
$('#datetimepicker-end').data('DateTimePicker').defaultDate(moment.utc(e.date).add(60, 'm'));
$('#datetimepicker-end').data('DateTimePicker').minDate(e.date);
$('#datetimepicker-end').data('DateTimePicker').maxDate(moment.utc(e.date).add(maxrange, 'm'));
});
}
select_proper_transmitters(satellite);
$('#satellite-selection').bind('keyup change', function() {
var norad = $(this).find(':selected').data('norad');
select_proper_transmitters(norad);
satellite = $(this).find(':selected').data('norad');
select_proper_transmitters(satellite);
});
$('#calculate-observation').click( function(){
@ -35,12 +43,17 @@ $( document ).ready( function(){
$('#timeline').empty();
$('#hoverRes').hide();
$('#windows-data').empty();
var satellite = $('#satellite-selection').val();
var start_time = $('#datetimepicker-start input').val();
var end_time = $('#datetimepicker-end input').val();
var url = '/prediction_windows/' + satellite + '/' + start_time + '/' + end_time + '/';
if (obs_filter) {
url = '/prediction_windows/' + satellite + '/' + start_time + '/' + end_time + '/' + ground_station + '/';
}
$.ajax({
url: '/prediction_windows/' + satellite + '/' + start_time + '/' + end_time + '/',
url: url,
beforeSend: function() { $('#loading').show(); }
}).done(function(data) {
$('#loading').hide();

View File

@ -21,112 +21,136 @@
</div>
</div>
<form class="form-horizontal" role="form" action="{% url 'base:observation_new' %}" method="post">{% csrf_token %}
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="col-sm-3 control-label">Satellite</label>
<div class="col-sm-9">
<select id="satellite-selection" class="form-control" name="satellite" autocomplete="off">
<option value="" disabled selected>Select a satellite</option>
<form class="form-horizontal" role="form" action="{% url 'base:observation_new' %}"
method="post" data-obs-filter="{{ obs_filter.exists|lower }}" id="form-obs">{% csrf_token %}
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="col-sm-3 control-label">Satellite</label>
<div class="col-sm-9">
{% if obs_filter.exists %}
{% for satellite in satellites %}
{% ifequal satellite.norad_cat_id obs_filter.norad %}
<input type="text" class="form-control" name="satellite-name"
readonly value="{{ satellite.norad_cat_id }} - {{ satellite.name }}">
{% endifequal %}
{% endfor %}
<input type="hidden" name="satellite" value="{{ obs_filter.norad }}">
{% else %}
<select id="satellite-selection" class="form-control" name="satellite" autocomplete="off">
<option value="" selected>Select a satellite</option>
{% for satellite in satellites %}
<option data-norad="{{ satellite.norad_cat_id }}" value="{{ satellite.norad_cat_id }}">
{{ satellite.norad_cat_id }} - {{ satellite.name }}
</option>
{% endfor %}
</select>
{% endif %}
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Transmitter</label>
<div class="col-sm-9">
<select id="transmitter-selection" class="form-control" disabled name="transmitter" autocomplete="off">
<option id="no-transmitter" value="" disabled selected>No transmitter available</option>
{% for transmitter in transmitters %}
<option data-satellite="{{ transmitter.satellite.norad_cat_id }}"
value="{{ transmitter.id }}" >
{{ transmitter.description }} - {{ transmitter.downlink_low|frq }} - {{ transmitter.mode|default:"" }}
</option>
{% endfor %}
</select>
{% for satellite in satellites %}
<option data-norad="{{ satellite.norad_cat_id }}" value="{{ satellite.norad_cat_id }}"
{% ifequal satellite.norad_cat_id norad %}selected{% endifequal %}>
{{ satellite.norad_cat_id }} - {{ satellite.name }}
</option>
<small class="tle" data-norad="{{ satellite.norad_cat_id }}">
{% if satellite.tle_no %}
Using TLE {{ satellite.tle_no }} issued
{{ satellite.tle_epoch|timesince }} ago
{% endif %}
</small>
{% endfor %}
</select>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Transmitter</label>
<div class="col-sm-9">
<select id="transmitter-selection" class="form-control" disabled name="transmitter" autocomplete="off">
<option id="no-transmitter" value="" disabled selected>No transmitter available</option>
{% for transmitter in transmitters %}
<option data-satellite="{{ transmitter.satellite.norad_cat_id }}"
value="{{ transmitter.id }}" >
{{ transmitter.description }} - {{ transmitter.downlink_low|frq }} - {{ transmitter.mode|default:"" }}
</option>
{% endfor %}
</select>
{% for satellite in satellites %}
<small class="tle" data-norad="{{ satellite.norad_cat_id }}">
{% if satellite.tle_no %}
Using TLE {{ satellite.tle_no }} issued
{{ satellite.tle_epoch|timesince }} ago
<div class="col-md-6">
<div class="form-group">
<label class="col-sm-3 control-label">Start Time</label>
<div class="col-sm-9">
<div class='input-group date' id="datetimepicker-start"
data-date-minstart="{{ date_min_start }}">
{% if obs_filter.exists %}
<input type="text" class="form-control" name="start-time"
value="{{ obs_filter.start_date}}" readonly>
{% else %}
<input type="text"
class="form-control"
name="start-time"
data-date-format="YYYY-MM-DD HH:mm"
autocomplete="off">
{% endif %}
</small>
{% endfor %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="col-sm-3 control-label">Start Time</label>
<div class="col-sm-9">
<div class='input-group date' id="datetimepicker-start"
data-date-minstart="{{ date_min_start }}">
<input type="text"
class="form-control"
name="start-time"
data-date-format="YYYY-MM-DD HH:mm"
autocomplete="off">
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">End Time</label>
<div class="col-sm-9">
<div class='input-group date' id="datetimepicker-end"
data-date-maxrange="{{ date_max_range }}"
data-date-minend="{{ date_min_end }}">
<input type="text"
class="form-control"
name="end-time"
data-date-format="YYYY-MM-DD HH:mm"
autocomplete="off">
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
<div class="form-group">
<label class="col-sm-3 control-label">End Time</label>
<div class="col-sm-9">
<div class='input-group date' id="datetimepicker-end"
data-date-maxrange="{{ date_max_range }}"
data-date-minend="{{ date_min_end }}">
{% if obs_filter.exists %}
<input type="text" class="form-control" name="end-time"
value="{{ obs_filter.end_date}}" readonly>
{% else %}
<input type="text"
class="form-control"
name="end-time"
data-date-format="YYYY-MM-DD HH:mm"
autocomplete="off">
{% endif %}
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<button type="button" id="calculate-observation" class="btn btn-primary pull-right">
Calculate Observation
</button>
</div>
</div>
{% if obs_filter.exists %}
<input type="hidden" name="ground_station" value="{{ obs_filter.ground_station }}">
{% endif %}
<div class="row calculation-result">
<div class="col-md-12">
<h3>Calculated Timeline</h3>
<div id="loading" class="notice">Calculating</div>
<div id="timeline"></div>
<div id="hoverRes">
<div class="coloredDiv"></div>
<div id="name"></div>
<div id="scrolled_date"></div>
<div class="row">
<div class="col-md-12">
<button type="button" id="calculate-observation" class="btn btn-primary pull-right">
Calculate Observation
</button>
</div>
<div id="windows-data"></div>
</div>
</div>
<div class="row calculation-result">
<div class="col-md-12">
<button type="submit" id="schedule-observation" class="btn btn-success pull-right" disabled="True">
Schedule Observation
</button>
<div class="row calculation-result">
<div class="col-md-12">
<h3>Calculated Timeline</h3>
<div id="loading" class="notice">Calculating</div>
<div id="timeline"></div>
<div id="hoverRes">
<div class="coloredDiv"></div>
<div id="name"></div>
<div id="scrolled_date"></div>
</div>
<div id="windows-data"></div>
</div>
</div>
<div class="row calculation-result">
<div class="col-md-12">
<button type="submit" id="schedule-observation" class="btn btn-success pull-right" disabled="True">
Schedule Observation
</button>
</div>
</div>
</div>
</form>
<!-- UTC Modal -->

View File

@ -122,7 +122,7 @@
Data payload <a href="{% url 'base:observation_data_view' id=data.id %}">#{{ data.id }}</a> ~
Groud Station:
Ground Station:
<a href="{% url 'base:station_view' id=data.ground_station.id %}">
{{ data.ground_station }}
</a>
@ -154,8 +154,16 @@
Waiting for data
</div>
{% endif %}
{% if data.waterfall %}
<div id="waterfall-{{ data.id }}" class="collapse well waterfall">
<img class="img-responsive waterfall" src="{{ MEDIA_URL }}{{ data.waterfall }}" alt="waterfall">
</div>
{% endif %}
</div>
<div class="panel-footer">
{% if not data.payload and not data.waterfall %}
&nbsp;
{% endif %}
{% if data.payload %}
<button type="button" class="btn btn-primary btn-xs playpause">
<span class="glyphicon glyphicon-play"></span>
@ -170,8 +178,13 @@
<span class="glyphicon glyphicon-download"></span> Data
</button>
</a>
{% else %}
&nbsp;
{% endif %}
{% if data.waterfall %}
<button type="button" class="btn btn-default btn-xs" role="button"
data-toggle="collapse" href="#waterfall-{{ data.id }}" aria-expanded="false"
aria-controls="waterfall-{{ data.id }}">
<span class="glyphicon glyphicon-picture"></span> Waterfall
</button>
{% endif %}
<span class="pull-right hidden-xs">

View File

@ -169,13 +169,14 @@
<th>Rise Time</th>
<th>Max Altitude</th>
<th>Rising Azimuth</th>
<th></th>
</thead>
<tbody>
{% for nextpass in nextpasses %}
<tr>
<td>
<a href="#" data-toggle="modal" data-target="#SatelliteModal" data-id="{{ nextpass.norad_cat_id }}">
{{ nextpass.name }}
{{ nextpass.norad_cat_id }} - {{ nextpass.name }}
</a>
</td>
<td>
@ -187,6 +188,14 @@
<td>
{{ nextpass.azr }}°
</td>
<td>
{% if nextpass.valid %}
<a href="{% url 'base:observation_new' %}?norad={{ nextpass.norad_cat_id }}&ground_station={{ station.id }}&start_date={{ nextpass.tr|truncatesecs }}&end_date={{ nextpass.ts|truncatesecs }}"
class="btn btn-default">schedule</a>
{% else %}
<a href="#" class="btn btn-default" disabled>schedule</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>

View File

@ -17,7 +17,7 @@
<span class="satellite-title"></span>
<span class="satellite-names"></span>
</div>
<div class=satellite-info">
<div class="satellite-info">
<span class="satellite-id"></span>
</div>
<ul class="list-unstyled">

View File

@ -1,5 +1,5 @@
# Basic stuff
django==1.10.4
django==1.10.5
django-shortuuidfield==0.1.3
django_compressor==2.1
django-bower==5.2.0
@ -8,7 +8,7 @@ django-bower==5.2.0
unicode-slugify==0.1.3
django-autoslug==1.9.3
django_dotenv==1.4.1
dj-database-url==0.4.1
dj-database-url==0.4.2
opbeat==3.5.2
# Security
@ -27,5 +27,5 @@ git+https://github.com/comzeradd/orbit.git@cache##egg=orbit
# API
djangorestframework==3.5.3
Markdown==2.6.7
Markdown==2.6.8
django-filter==1.0.1

View File

@ -5,12 +5,12 @@
sqlparse==0.2.2
flake8==3.2.1
factory-boy==2.8.1
pytest==3.0.5
pytest==3.0.6
pytest-cov==2.4.0
pytest-django==3.1.2
pytest-xdist==1.15.0
python-coveralls==2.9.0
coverage==4.3.1
coverage==4.3.4
faker
docopts
mock==2.0.0