1
0
Fork 0

Refactor transmitter model

Signed-off-by: Alfredos-Panagiotis Damkalis <fredy@fredy.gr>
merge-requests/741/head
Alfredos-Panagiotis Damkalis 2019-05-02 23:05:25 +03:00
parent 90ae0e1a2b
commit 03f669711e
18 changed files with 562 additions and 373 deletions

View File

@ -10,8 +10,8 @@ class ObservationViewFilter(FilterSet):
class Meta:
model = Observation
fields = ['id', 'ground_station', 'satellite__norad_cat_id', 'transmitter',
'vetted_status', 'vetted_user']
fields = ['id', 'ground_station', 'satellite__norad_cat_id', 'transmitter_uuid',
'transmitter_mode', 'transmitter_type', 'vetted_status', 'vetted_user']
class StationViewFilter(FilterSet):
@ -23,4 +23,4 @@ class StationViewFilter(FilterSet):
class TransmitterViewFilter(FilterSet):
class Meta:
model = Transmitter
fields = ['uuid', 'satellite__norad_cat_id']
fields = ['uuid', 'sync_to_db']

View File

@ -11,6 +11,7 @@ class DemodDataSerializer(serializers.ModelSerializer):
class ObservationSerializer(serializers.ModelSerializer):
transmitter = serializers.SerializerMethodField()
transmitter_updated = serializers.DateTimeField(source='transmitter_created')
norad_cat_id = serializers.SerializerMethodField()
station_name = serializers.SerializerMethodField()
station_lat = serializers.SerializerMethodField()
@ -24,12 +25,23 @@ class ObservationSerializer(serializers.ModelSerializer):
'payload', 'waterfall', 'demoddata', 'station_name', 'station_lat',
'station_lng', 'station_alt', 'vetted_status', 'archived', 'archive_url',
'client_version', 'client_metadata', 'vetted_user', 'vetted_datetime',
'rise_azimuth', 'set_azimuth', 'max_altitude', 'tle')
'rise_azimuth', 'set_azimuth', 'max_altitude', 'transmitter_uuid',
'transmitter_description', 'transmitter_type', 'transmitter_uplink_low',
'transmitter_uplink_high', 'transmitter_uplink_drift',
'transmitter_downlink_low', 'transmitter_downlink_high',
'transmitter_downlink_drift', 'transmitter_mode', 'transmitter_invert',
'transmitter_baud', 'transmitter_updated', 'tle')
read_only_fields = ['id', 'start', 'end', 'observation', 'ground_station',
'transmitter', 'norad_cat_id', 'archived', 'archive_url',
'station_name', 'station_lat', 'station_lng', 'vetted_user',
'station_alt', 'vetted_status', 'vetted_datetime', 'rise_azimuth',
'set_azimuth', 'max_altitude', 'tle']
'set_azimuth', 'max_altitude', 'transmitter_uuid',
'transmitter_description', 'transmitter_type',
'transmitter_uplink_low', 'transmitter_uplink_high',
'transmitter_uplink_drift', 'transmitter_downlink_low',
'transmitter_downlink_high', 'transmitter_downlink_drift',
'transmitter_mode', 'transmitter_invert', 'transmitter_baud',
'transmitter_created', 'transmitter_updated', 'tle']
def update(self, instance, validated_data):
validated_data.pop('demoddata')
@ -38,7 +50,7 @@ class ObservationSerializer(serializers.ModelSerializer):
def get_transmitter(self, obj):
try:
return obj.transmitter.uuid
return obj.transmitter_uuid
except AttributeError:
return ''
@ -133,15 +145,15 @@ class JobSerializer(serializers.ModelSerializer):
'frequency', 'mode', 'transmitter', 'baud')
def get_frequency(self, obj):
frequency = obj.transmitter.downlink_low
frequency_drift = obj.transmitter.downlink_drift
frequency = obj.transmitter_downlink_low
frequency_drift = obj.transmitter_downlink_drift
if frequency_drift is None:
return frequency
else:
return int(round(frequency + ((frequency * frequency_drift) / float(pow(10, 9)))))
def get_transmitter(self, obj):
return obj.transmitter.uuid
return obj.transmitter_uuid
def get_tle0(self, obj):
return obj.tle.tle0
@ -154,30 +166,16 @@ class JobSerializer(serializers.ModelSerializer):
def get_mode(self, obj):
try:
return obj.transmitter.mode.name
return obj.transmitter_mode.name
except AttributeError:
return ''
def get_baud(self, obj):
return obj.transmitter.baud
return obj.transmitter_baud
class TransmitterSerializer(serializers.ModelSerializer):
mode = serializers.SerializerMethodField()
norad_cat_id = serializers.SerializerMethodField()
class Meta:
model = Transmitter
fields = ('uuid', 'description', 'alive', 'type', 'uplink_low', 'uplink_high',
'uplink_drift', 'downlink_low', 'downlink_high', 'downlink_drift',
'mode', 'invert', 'baud', 'satellite', 'norad_cat_id',
'success_rate', 'bad_rate', 'unvetted_rate', 'good_count',
'bad_count', 'unvetted_count', 'data_count')
def get_mode(self, obj):
if obj.mode is None:
return "No Mode"
return obj.mode.name
def get_norad_cat_id(self, obj):
return obj.satellite.norad_cat_id
fields = ('uuid', 'sync_to_db')

View File

@ -8,7 +8,6 @@ from rest_framework.utils.encoders import JSONEncoder
from network.base.tests import (
ObservationFactory,
SatelliteFactory,
TransmitterFactory,
StationFactory,
AntennaFactory
)
@ -21,14 +20,11 @@ class JobViewApiTest(TestCase):
"""
observation = None
satellites = []
transmitters = []
stations = []
def setUp(self):
for x in xrange(1, 10):
self.satellites.append(SatelliteFactory())
for x in xrange(1, 10):
self.transmitters.append(TransmitterFactory())
for x in xrange(1, 10):
self.stations.append(StationFactory())
self.observation = ObservationFactory()

View File

@ -68,14 +68,10 @@ class TleAdmin(admin.ModelAdmin):
@admin.register(Transmitter)
class TransmitterAdmin(admin.ModelAdmin):
list_display = ('uuid', 'description', 'satellite', 'type', 'alive', 'mode', 'uplink_low',
'uplink_high', 'uplink_drift', 'downlink_low', 'downlink_high',
'downlink_drift', 'baud', 'sync_to_db')
search_fields = ('uuid', 'satellite__name', 'satellite__norad_cat_id')
list_filter = ('type', 'mode', 'alive', 'sync_to_db')
readonly_fields = ('uuid', 'description', 'satellite', 'type', 'uplink_low', 'uplink_high',
'uplink_drift', 'downlink_low', 'downlink_high', 'downlink_drift',
'baud', 'invert', 'alive', 'mode')
list_display = ('uuid', 'sync_to_db')
search_fields = ('uuid',)
list_filter = ('sync_to_db',)
readonly_fields = ('uuid', 'sync_to_db')
class DataDemodInline(admin.TabularInline):
@ -84,7 +80,7 @@ class DataDemodInline(admin.TabularInline):
@admin.register(Observation)
class ObservationAdmin(admin.ModelAdmin):
list_display = ('id', 'author', 'satellite', 'transmitter', 'start', 'end')
list_display = ('id', 'author', 'satellite', 'transmitter_uuid', 'start', 'end')
list_filter = ('start', 'end')
search_fields = ('satellite', 'author')
inlines = [

View File

@ -0,0 +1,35 @@
import requests
from django.conf import settings
db_api_url = settings.DB_API_ENDPOINT
def transmitters_api_request(url):
if len(db_api_url) == 0:
return None
try:
request = requests.get(url)
except requests.exceptions.RequestException:
return None
return request.json()
def get_transmitter_by_uuid(uuid):
transmitters_url = "{}transmitters/?uuid={}".format(db_api_url, uuid)
return transmitters_api_request(transmitters_url)
def get_transmitters_by_norad_id(norad_id):
transmitters_url = "{}transmitters/?satellite__norad_cat_id={}".format(db_api_url, norad_id)
return transmitters_api_request(transmitters_url)
def get_transmitters_by_status(status):
transmitters_url = "{}transmitters/?status={}".format(db_api_url, status)
return transmitters_api_request(transmitters_url)
def get_transmitters():
transmitters_url = "{}transmitters".format(db_api_url)
return transmitters_api_request(transmitters_url)

View File

@ -1,9 +1,5 @@
from rest_framework.authtoken.models import Token
def get_apikey(user):
try:
token = Token.objects.get(user=user)
except Token.DoesNotExist:
token = Token.objects.create(user=user)
return token
def downlink_low_is_in_range(antenna, transmitter):
if transmitter['downlink_low'] is not None:
return antenna.frequency <= transmitter['downlink_low'] <= antenna.frequency_max
else:
return False

View File

@ -59,30 +59,11 @@ class Command(BaseCommand):
# Fetch Transmitters
for transmitter in r_transmitters.json():
norad_cat_id = transmitter['norad_cat_id']
uuid = transmitter['uuid']
description = transmitter['description']
mode_id = transmitter['mode_id']
try:
sat = Satellite.objects.get(norad_cat_id=norad_cat_id)
except Satellite.DoesNotExist:
self.stdout.write('Satellite {0} not present'.format(norad_cat_id))
transmitter.pop('norad_cat_id')
try:
mode = Mode.objects.get(id=mode_id)
except Mode.DoesNotExist:
mode = None
try:
existing_transmitter = Transmitter.objects.get(uuid=uuid)
existing_transmitter.__dict__.update(transmitter)
existing_transmitter.satellite = sat
existing_transmitter.save()
self.stdout.write('Transmitter {0}-{1} updated'.format(uuid, description))
Transmitter.objects.get(uuid=uuid)
self.stdout.write('Transmitter {0} already exists'.format(uuid))
except Transmitter.DoesNotExist:
new_transmitter = Transmitter.objects.create(**transmitter)
new_transmitter.satellite = sat
new_transmitter.mode = mode
new_transmitter.save()
self.stdout.write('Transmitter {0}-{1} created'.format(uuid, description))
Transmitter.objects.create(uuid=uuid)
self.stdout.write('Transmitter {0} created'.format(uuid))

View File

@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-04-22 09:41
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import shortuuidfield.fields
def from_transmitter_to_observation(apps, schema_editor):
Observation = apps.get_model('base', 'Observation')
observations = Observation.objects.all()
for observation in observations:
observation.transmitter_uuid = observation.transmitter.uuid
observation.transmitter_description = observation.transmitter.description
observation.transmitter_type = observation.transmitter.type
observation.transmitter_uplink_low = observation.transmitter.uplink_low
observation.transmitter_uplink_high = observation.transmitter.uplink_high
observation.transmitter_uplink_drift = observation.transmitter.uplink_drift
observation.transmitter_downlink_low = observation.transmitter.downlink_low
observation.transmitter_downlink_high = observation.transmitter.downlink_high
observation.transmitter_downlink_drift = observation.transmitter.downlink_drift
observation.transmitter_mode = observation.transmitter.mode
observation.transmitter_invert = observation.transmitter.invert
observation.transmitter_baud = observation.transmitter.baud
observation.save()
def reverse_from_transmitter_to_observation(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('base', '0057_no_null_demoddata_observation_field'),
]
operations = [
migrations.AddField(
model_name='observation',
name='transmitter_baud',
field=models.FloatField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AddField(
model_name='observation',
name='transmitter_created',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name='observation',
name='transmitter_description',
field=models.TextField(default=b''),
),
migrations.AddField(
model_name='observation',
name='transmitter_downlink_drift',
field=models.IntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='observation',
name='transmitter_downlink_high',
field=models.BigIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='observation',
name='transmitter_downlink_low',
field=models.BigIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='observation',
name='transmitter_invert',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='observation',
name='transmitter_mode',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='observations', to='base.Mode'),
),
migrations.AddField(
model_name='observation',
name='transmitter_type',
field=models.CharField(choices=[(b'Transmitter', b'Transmitter'), (b'Transceiver', b'Transceiver'), (b'Transponder', b'Transponder')], default=b'Transmitter', max_length=11),
),
migrations.AddField(
model_name='observation',
name='transmitter_uplink_drift',
field=models.IntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='observation',
name='transmitter_uplink_high',
field=models.BigIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='observation',
name='transmitter_uplink_low',
field=models.BigIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='observation',
name='transmitter_uuid',
field=shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, max_length=22),
),
migrations.RunPython(from_transmitter_to_observation, reverse_from_transmitter_to_observation),
migrations.RemoveField(
model_name='observation',
name='transmitter',
),
migrations.RemoveField(
model_name='transmitter',
name='alive',
),
migrations.RemoveField(
model_name='transmitter',
name='baud',
),
migrations.RemoveField(
model_name='transmitter',
name='description',
),
migrations.RemoveField(
model_name='transmitter',
name='downlink_drift',
),
migrations.RemoveField(
model_name='transmitter',
name='downlink_high',
),
migrations.RemoveField(
model_name='transmitter',
name='downlink_low',
),
migrations.RemoveField(
model_name='transmitter',
name='invert',
),
migrations.RemoveField(
model_name='transmitter',
name='mode',
),
migrations.RemoveField(
model_name='transmitter',
name='satellite',
),
migrations.RemoveField(
model_name='transmitter',
name='type',
),
migrations.RemoveField(
model_name='transmitter',
name='uplink_drift',
),
migrations.RemoveField(
model_name='transmitter',
name='uplink_high',
),
migrations.RemoveField(
model_name='transmitter',
name='uplink_low',
),
]

View File

@ -17,9 +17,8 @@ from django.utils.html import format_html
from django.utils.timezone import now
from network.users.models import User
from network.base.helpers import get_apikey
from network.base.managers import ObservationManager
from rest_framework.authtoken.models import Token
ANTENNA_BANDS = ['HF', 'VHF', 'UHF', 'L', 'S', 'C', 'X', 'KU']
ANTENNA_TYPES = (
@ -51,6 +50,7 @@ STATION_STATUSES = (
(0, 'Offline'),
)
SATELLITE_STATUS = ['alive', 'dead', 're-entered']
TRANSMITTER_STATUS = ['active', 'inactive', 'invalid']
TRANSMITTER_TYPE = ['Transmitter', 'Transceiver', 'Transponder']
@ -236,7 +236,11 @@ class Station(models.Model):
@property
def apikey(self):
return get_apikey(user=self.owner)
try:
token = Token.objects.get(user=self.owner)
except Token.DoesNotExist:
token = Token.objects.create(user=self.owner)
return token
def __unicode__(self):
return "%d - %s" % (self.pk, self.name)
@ -382,107 +386,13 @@ post_save.connect(_tle_post_save, sender=Tle)
class Transmitter(models.Model):
"""Model for antennas transponders."""
uuid = ShortUUIDField(db_index=True)
description = models.TextField()
alive = models.BooleanField(default=True)
type = models.CharField(choices=zip(TRANSMITTER_TYPE, TRANSMITTER_TYPE),
max_length=11, default='Transmitter')
uplink_low = models.BigIntegerField(blank=True, null=True)
uplink_high = models.BigIntegerField(blank=True, null=True)
uplink_drift = models.IntegerField(blank=True, null=True)
downlink_low = models.BigIntegerField(blank=True, null=True)
downlink_high = models.BigIntegerField(blank=True, null=True)
downlink_drift = models.IntegerField(blank=True, null=True)
mode = models.ForeignKey(Mode, related_name='transmitters', blank=True,
null=True, on_delete=models.SET_NULL)
invert = models.BooleanField(default=False)
baud = models.FloatField(validators=[MinValueValidator(0)], null=True, blank=True)
satellite = models.ForeignKey(Satellite, related_name='transmitters',
on_delete=models.CASCADE, null=True, blank=True)
sync_to_db = models.BooleanField(default=False)
@property
def data_count(self):
return Observation.objects.filter(transmitter=self).exclude(vetted_status='failed').count()
@property
def good_count(self):
data = cache.get('tr-{0}-suc-count'.format(self.uuid))
if data is None:
obs = Observation.objects.filter(transmitter=self)
data = obs.filter(vetted_status='good').count()
cache.set('tr-{0}-suc-count'.format(self.uuid), data, 3600)
return data
return data
@property
def bad_count(self):
data = cache.get('tr-{0}-bad-count'.format(self.uuid))
if data is None:
obs = Observation.objects.filter(transmitter=self)
data = obs.filter(vetted_status='bad').count()
cache.set('tr-{0}-bad-count'.format(self.uuid), data, 3600)
return data
return data
@property
def unvetted_count(self):
data = cache.get('tr-{0}-unk-count'.format(self.uuid))
if data is None:
obs = Observation.objects.filter(transmitter=self)
data = obs.filter(vetted_status='unknown').count()
cache.set('tr-{0}-unk-count'.format(self.uuid), data, 3600)
return data
return data
@property
def success_rate(self):
rate = cache.get('tr-{0}-suc-rate'.format(self.uuid))
if rate is None:
try:
rate = int(100 * (float(self.good_count) / float(self.data_count)))
cache.set('tr-{0}-suc-rate'.format(self.uuid), rate, 3600)
return rate
except (ZeroDivisionError, TypeError):
cache.set('tr-{0}-suc-rate'.format(self.uuid), 0, 3600)
return 0
return rate
@property
def bad_rate(self):
rate = cache.get('tr-{0}-bad-rate'.format(self.uuid))
if rate is None:
try:
rate = int(100 * (float(self.bad_count) / float(self.data_count)))
cache.set('tr-{0}-bad-rate'.format(self.uuid), rate, 3600)
return rate
except (ZeroDivisionError, TypeError):
cache.set('tr-{0}-bad-rate'.format(self.uuid), 0, 3600)
return 0
return rate
@property
def unvetted_rate(self):
rate = cache.get('tr-{0}-unk-rate'.format(self.uuid))
if rate is None:
try:
rate = int(100 * (float(self.unvetted_count) / float(self.data_count)))
cache.set('tr-{0}-unk-rate'.format(self.uuid), rate, 3600)
return rate
except (ZeroDivisionError, TypeError):
cache.set('tr-{0}-unk-rate'.format(self.uuid), 0, 3600)
return 0
return rate
def __unicode__(self):
return self.description
class Observation(models.Model):
"""Model for SatNOGS observations."""
satellite = models.ForeignKey(Satellite, related_name='observations',
on_delete=models.SET_NULL, null=True, blank=True)
transmitter = models.ForeignKey(Transmitter, related_name='observations',
on_delete=models.SET_NULL, null=True, blank=True)
tle = models.ForeignKey(Tle, related_name='observations',
on_delete=models.SET_NULL, null=True, blank=True)
author = models.ForeignKey(User, related_name='observations',
@ -507,6 +417,21 @@ class Observation(models.Model):
archived = models.BooleanField(default=False)
archive_identifier = models.CharField(max_length=255, blank=True)
archive_url = models.URLField(blank=True, null=True)
transmitter_uuid = ShortUUIDField(db_index=True)
transmitter_description = models.TextField(default='')
transmitter_type = models.CharField(choices=zip(TRANSMITTER_TYPE, TRANSMITTER_TYPE),
max_length=11, default='Transmitter')
transmitter_uplink_low = models.BigIntegerField(blank=True, null=True)
transmitter_uplink_high = models.BigIntegerField(blank=True, null=True)
transmitter_uplink_drift = models.IntegerField(blank=True, null=True)
transmitter_downlink_low = models.BigIntegerField(blank=True, null=True)
transmitter_downlink_high = models.BigIntegerField(blank=True, null=True)
transmitter_downlink_drift = models.IntegerField(blank=True, null=True)
transmitter_mode = models.ForeignKey(Mode, blank=True, null=True, on_delete=models.SET_NULL,
related_name='observations')
transmitter_invert = models.BooleanField(default=False)
transmitter_baud = models.FloatField(validators=[MinValueValidator(0)], blank=True, null=True)
transmitter_created = models.DateTimeField(default=now)
objects = ObservationManager.as_manager()

View File

@ -3,7 +3,7 @@ from datetime import timedelta
from django.conf import settings
from django.utils.timezone import now, make_aware, utc
from network.base.models import Satellite, Station, Tle, Transmitter, Observation
from network.base.models import Mode, Satellite, Station, Tle, Observation
from network.base.perms import schedule_station_perms
import ephem
@ -289,12 +289,7 @@ def predict_available_observation_windows(station, min_horizon, overlapped, tle,
return passes_found, station_windows
def create_new_observation(station_id,
sat_id,
trans_id,
start_time,
end_time,
author):
def create_new_observation(station_id, sat_id, transmitter, start_time, end_time, author):
ground_station = Station.objects.get(id=station_id)
scheduled_obs = Observation.objects.filter(ground_station=ground_station).filter(end__gt=now())
window = resolve_overlaps(scheduled_obs, start_time, end_time)
@ -303,7 +298,6 @@ def create_new_observation(station_id,
raise ObservationOverlapError
sat = Satellite.objects.get(norad_cat_id=sat_id)
trans = Transmitter.objects.get(uuid=trans_id)
tle = Tle.objects.get(id=sat.latest_tle.id)
sat_ephem = ephem.readtle(str(sat.latest_tle.tle0),
@ -319,13 +313,25 @@ def create_new_observation(station_id,
rise_azimuth = get_azimuth(observer, sat_ephem, start_time)
max_altitude = get_elevation(observer, sat_ephem, mid_pass_time)
set_azimuth = get_azimuth(observer, sat_ephem, end_time)
try:
mode = Mode.objects.get(id=transmitter['mode_id'])
except Mode.DoesNotExist:
mode = Mode.objects.create(id=transmitter['mode_id'], name=transmitter['mode'])
return Observation(satellite=sat, transmitter=trans, tle=tle, author=author,
start=start_time, end=end_time,
ground_station=ground_station,
rise_azimuth=rise_azimuth,
max_altitude=max_altitude,
set_azimuth=set_azimuth)
return Observation(
satellite=sat, tle=tle, author=author, start=start_time, end=end_time,
ground_station=ground_station, rise_azimuth=rise_azimuth, max_altitude=max_altitude,
set_azimuth=set_azimuth, transmitter_uuid=transmitter['uuid'],
transmitter_description=transmitter['description'], transmitter_type=transmitter['type'],
transmitter_uplink_low=transmitter['uplink_low'],
transmitter_uplink_high=transmitter['uplink_high'],
transmitter_uplink_drift=transmitter['uplink_drift'],
transmitter_downlink_low=transmitter['downlink_low'],
transmitter_downlink_high=transmitter['downlink_high'],
transmitter_downlink_drift=transmitter['downlink_drift'],
transmitter_mode=mode, transmitter_invert=transmitter['invert'],
transmitter_baud=transmitter['baud'], transmitter_created=transmitter['updated']
)
def get_available_stations(stations, downlink, user):

View File

@ -0,0 +1,93 @@
from django.core.cache import cache
from network.base.models import Observation
def transmitter_total_count(uuid):
return Observation.objects.filter(transmitter_uuid=uuid) \
.exclude(vetted_status='failed') \
.count()
def transmitter_good_count(uuid):
data = cache.get('tr-{0}-suc-count'.format(uuid))
if data is None:
obs = Observation.objects.filter(transmitter_uuid=uuid)
data = obs.filter(vetted_status='good').count()
cache.set('tr-{0}-suc-count'.format(uuid), data, 3600)
return data
return data
def transmitter_bad_count(uuid):
data = cache.get('tr-{0}-bad-count'.format(uuid))
if data is None:
obs = Observation.objects.filter(transmitter_uuid=uuid)
data = obs.filter(vetted_status='bad').count()
cache.set('tr-{0}-bad-count'.format(uuid), data, 3600)
return data
return data
def transmitter_unvetted_count(uuid):
data = cache.get('tr-{0}-unk-count'.format(uuid))
if data is None:
obs = Observation.objects.filter(transmitter_uuid=uuid)
data = obs.filter(vetted_status='unknown').count()
cache.set('tr-{0}-unk-count'.format(uuid), data, 3600)
return data
return data
def transmitter_success_rate(uuid):
rate = cache.get('tr-{0}-suc-rate'.format(uuid))
if rate is None:
try:
ratio = float(transmitter_good_count(uuid)) / float(transmitter_total_count(uuid))
rate = int(100 * ratio)
cache.set('tr-{0}-suc-rate'.format(uuid), rate, 3600)
return rate
except (ZeroDivisionError, TypeError):
cache.set('tr-{0}-suc-rate'.format(uuid), 0, 3600)
return 0
return rate
def transmitter_bad_rate(uuid):
rate = cache.get('tr-{0}-bad-rate'.format(uuid))
if rate is None:
try:
ratio = float(transmitter_bad_count(uuid)) / float(transmitter_total_count(uuid))
rate = int(100 * ratio)
cache.set('tr-{0}-bad-rate'.format(uuid), rate, 3600)
return rate
except (ZeroDivisionError, TypeError):
cache.set('tr-{0}-bad-rate'.format(uuid), 0, 3600)
return 0
return rate
def transmitter_unvetted_rate(uuid):
rate = cache.get('tr-{0}-unk-rate'.format(uuid))
if rate is None:
try:
ratio = float(transmitter_unvetted_count(uuid)) / float(transmitter_total_count(uuid))
rate = int(100 * ratio)
cache.set('tr-{0}-unk-rate'.format(uuid), rate, 3600)
return rate
except (ZeroDivisionError, TypeError):
cache.set('tr-{0}-unk-rate'.format(uuid), 0, 3600)
return 0
return rate
def transmitter_stats_by_uuid(uuid):
return {
'total_count': transmitter_total_count(uuid),
'unvetted_count': transmitter_unvetted_count(uuid),
'good_count': transmitter_good_count(uuid),
'bad_count': transmitter_bad_count(uuid),
'success_rate': transmitter_success_rate(uuid),
'bad_rate': transmitter_bad_rate(uuid),
'unvetted_rate': transmitter_unvetted_rate(uuid)
}

View File

@ -94,30 +94,12 @@ def fetch_data():
# Fetch Transmitters
for transmitter in json.loads(transmitters):
norad_cat_id = transmitter['norad_cat_id']
uuid = transmitter['uuid']
mode_id = transmitter['mode_id']
try:
sat = Satellite.objects.get(norad_cat_id=norad_cat_id)
except Satellite.DoesNotExist:
continue
transmitter.pop('norad_cat_id')
try:
mode = Mode.objects.get(id=mode_id)
except Mode.DoesNotExist:
mode = None
try:
existing_transmitter = Transmitter.objects.get(uuid=uuid)
existing_transmitter.__dict__.update(transmitter)
existing_transmitter.satellite = sat
existing_transmitter.save()
Transmitter.objects.get(uuid=uuid)
except Transmitter.DoesNotExist:
new_transmitter = Transmitter.objects.create(**transmitter)
new_transmitter.satellite = sat
new_transmitter.mode = mode
new_transmitter.save()
Transmitter.objects.create(uuid=uuid)
@app.task(ignore_result=True)
@ -172,9 +154,10 @@ def clean_observations():
def sync_to_db():
"""Task to send demod data to db / SiDS"""
q = now() - timedelta(days=1)
transmitters = Transmitter.objects.filter(sync_to_db=True).values_list('uuid', flat=True)
frames = DemodData.objects.filter(observation__end__gte=q,
copied_to_db=False,
observation__transmitter__sync_to_db=True)
observation__transmitter_uuid__in=transmitters)
for frame in frames:
try:
if not frame.is_image() and not frame.copied_to_db:

View File

@ -11,8 +11,7 @@ from django.test import TestCase, Client
from django.utils.timezone import now
from network.base.models import (ANTENNA_BANDS, ANTENNA_TYPES, OBSERVATION_STATUSES,
Mode, Antenna, Satellite, Tle, Station, Transmitter,
Observation, DemodData)
Mode, Antenna, Satellite, Tle, Station, Observation, DemodData)
from network.users.tests import UserFactory
@ -37,12 +36,6 @@ def generate_payload_name():
return filename
def get_valid_satellites():
qs = Transmitter.objects.all()
satellites = Satellite.objects.filter(transmitters__in=qs).distinct()
return satellites
class ModeFactory(factory.django.DjangoModelFactory):
"""Mode model factory."""
name = fuzzy.FuzzyText()
@ -110,26 +103,9 @@ class TleFactory(factory.django.DjangoModelFactory):
model = Tle
class TransmitterFactory(factory.django.DjangoModelFactory):
"""Transmitter model factory."""
description = fuzzy.FuzzyText()
alive = fuzzy.FuzzyChoice(choices=[True, False])
uplink_low = fuzzy.FuzzyInteger(200000000, 500000000, step=10000)
uplink_high = fuzzy.FuzzyInteger(200000000, 500000000, step=10000)
downlink_low = fuzzy.FuzzyInteger(200000000, 500000000, step=10000)
downlink_high = fuzzy.FuzzyInteger(200000000, 500000000, step=10000)
mode = factory.SubFactory(ModeFactory)
invert = fuzzy.FuzzyChoice(choices=[True, False])
baud = fuzzy.FuzzyInteger(4000, 22000, step=1000)
satellite = factory.SubFactory(SatelliteFactory)
class Meta:
model = Transmitter
class ObservationFactory(factory.django.DjangoModelFactory):
"""Observation model factory."""
satellite = factory.Iterator(get_valid_satellites())
satellite = factory.SubFactory(SatelliteFactory)
tle = factory.SubFactory(TleFactory)
author = factory.SubFactory(UserFactory)
start = fuzzy.FuzzyDateTime(now() - timedelta(days=3),
@ -144,10 +120,17 @@ class ObservationFactory(factory.django.DjangoModelFactory):
)
vetted_user = factory.SubFactory(UserFactory)
vetted_status = fuzzy.FuzzyChoice(choices=OBSERVATION_STATUS_IDS)
@factory.lazy_attribute
def transmitter(self):
return self.satellite.transmitters.all().order_by('?')[0]
transmitter_uuid = fuzzy.FuzzyText(length=20)
transmitter_description = fuzzy.FuzzyText()
transmitter_uplink_low = fuzzy.FuzzyInteger(200000000, 500000000, step=10000)
transmitter_uplink_high = fuzzy.FuzzyInteger(200000000, 500000000, step=10000)
transmitter_downlink_low = fuzzy.FuzzyInteger(200000000, 500000000, step=10000)
transmitter_downlink_high = fuzzy.FuzzyInteger(200000000, 500000000, step=10000)
transmitter_mode = factory.SubFactory(ModeFactory)
transmitter_invert = fuzzy.FuzzyChoice(choices=[True, False])
transmitter_baud = fuzzy.FuzzyInteger(4000, 22000, step=1000)
transmitter_created = fuzzy.FuzzyDateTime(now() - timedelta(days=100),
now() - timedelta(days=3))
class Meta:
model = Observation
@ -208,17 +191,15 @@ class ObservationsListViewTest(TestCase):
client = Client()
observations = []
satellites = []
transmitters = []
stations = []
def setUp(self):
# Clear the data and create some new random data
with transaction.atomic():
Observation.objects.all().delete()
Transmitter.objects.all().delete()
Satellite.objects.all().delete()
Mode.objects.all().delete()
self.satellites = []
self.transmitters = []
self.observations_bad = []
self.observations_good = []
self.observations_unvetted = []
@ -226,46 +207,43 @@ class ObservationsListViewTest(TestCase):
with transaction.atomic():
for x in xrange(1, 10):
self.satellites.append(SatelliteFactory())
for x in xrange(1, 10):
self.transmitters.append(TransmitterFactory())
for x in xrange(1, 10):
self.stations.append(StationFactory())
for x in xrange(1, 10):
for x in xrange(1, 5):
obs = ObservationFactory(vetted_status='bad')
self.observations_bad.append(obs)
self.observations.append(obs)
for x in xrange(1, 10):
for x in xrange(1, 5):
obs = ObservationFactory(vetted_status='good')
self.observations_good.append(obs)
self.observations.append(obs)
for x in xrange(1, 10):
for x in xrange(1, 5):
obs = ObservationFactory(vetted_status='unknown')
self.observations_unvetted.append(obs)
self.observations.append(obs)
def test_observations_list(self):
response = self.client.get('/observations/')
for x in self.observations:
self.assertContains(response, x.transmitter.mode.name)
self.assertContains(response, x.transmitter_mode.name)
def test_observations_list_select_bad(self):
response = self.client.get('/observations/?bad=1')
for x in self.observations_bad:
self.assertContains(response, x.transmitter.mode.name)
self.assertContains(response, x.transmitter_mode.name)
def test_observations_list_select_good(self):
response = self.client.get('/observations/?good=1')
for x in self.observations_good:
self.assertContains(response, x.transmitter.mode.name)
self.assertContains(response, x.transmitter_mode.name)
def test_observations_list_select_unvetted(self):
response = self.client.get('/observations/?unvetted=1')
for x in self.observations_unvetted:
self.assertContains(response, x.transmitter.mode.name)
self.assertContains(response, x.transmitter_mode.name)
class NotFoundErrorTest(TestCase):
@ -298,7 +276,6 @@ class ObservationViewTest(TestCase):
client = Client()
observation = None
satellites = []
transmitters = []
stations = []
user = None
@ -308,8 +285,6 @@ class ObservationViewTest(TestCase):
g.user_set.add(self.user)
for x in xrange(1, 10):
self.satellites.append(SatelliteFactory())
for x in xrange(1, 10):
self.transmitters.append(TransmitterFactory())
for x in xrange(1, 10):
self.stations.append(StationFactory())
self.observation = ObservationFactory()
@ -317,7 +292,7 @@ class ObservationViewTest(TestCase):
def test_observation(self):
response = self.client.get('/observations/%d/' % self.observation.id)
self.assertContains(response, self.observation.author.username)
self.assertContains(response, self.observation.transmitter.mode.name)
self.assertContains(response, self.observation.transmitter_mode.name)
@pytest.mark.django_db(transaction=True)
@ -329,15 +304,12 @@ class ObservationDeleteTest(TestCase):
user = None
observation = None
satellites = []
transmitters = []
def setUp(self):
self.user = UserFactory()
self.client.force_login(self.user)
for x in xrange(1, 10):
self.satellites.append(SatelliteFactory())
for x in xrange(1, 10):
self.transmitters.append(TransmitterFactory())
self.observation = ObservationFactory()
self.observation.author = self.user
self.observation.save()
@ -450,15 +422,12 @@ class ObservationModelTest(TestCase):
"""
observation = None
satellites = []
transmitters = []
user = None
admin = None
def setUp(self):
for x in xrange(1, 10):
self.satellites.append(SatelliteFactory())
for x in xrange(1, 10):
self.transmitters.append(TransmitterFactory())
self.observation = ObservationFactory()
self.observation.end = now()
self.observation.save()

View File

@ -16,15 +16,18 @@ from django.utils.text import slugify
from django.views.generic import ListView
from rest_framework import serializers, viewsets
from network.base.models import (Station, Transmitter, Observation,
Satellite, Antenna, StationStatusLog)
from network.users.models import User
from network.base.forms import StationForm, SatelliteFilterForm
from network.base.decorators import admin_required, ajax_required
from network.base.db_api import (get_transmitter_by_uuid, get_transmitters_by_norad_id,
get_transmitters_by_status)
from network.base.forms import StationForm, SatelliteFilterForm
from network.base.helpers import downlink_low_is_in_range
from network.base.models import Station, Observation, Satellite, Antenna, StationStatusLog
from network.base.scheduling import (create_new_observation, ObservationOverlapError,
predict_available_observation_windows, get_available_stations)
from network.base.perms import schedule_perms, schedule_station_perms, delete_perms, vet_perms
from network.base.tasks import update_all_tle, fetch_data
from network.base.stats import transmitter_stats_by_uuid
from network.users.models import User
class StationSerializer(serializers.ModelSerializer):
@ -249,6 +252,19 @@ def observation_new_post(request):
messages.error(request, 'Please select at least one observation.')
return redirect(reverse('base:observation_new'))
uuid = request.POST.get('transmitter', None)
if uuid is None:
messages.error(request, 'The selected Transmitter is not valid.')
return redirect(reverse('base:observation_new'))
else:
transmitter = get_transmitter_by_uuid(uuid)
if transmitter is None:
messages.error(request, 'Error in DB API connection please try again.')
return redirect(reverse('base:observation_new'))
elif len(transmitter) == 0:
messages.error(request, 'The selected Transmitter is not valid.')
return redirect(reverse('base:observation_new'))
new_observations = []
for item in range(total):
try:
@ -282,7 +298,7 @@ def observation_new_post(request):
station_id = request.POST.get('{}-station'.format(item))
observation = create_new_observation(station_id=station_id,
sat_id=request.POST.get('satellite'),
trans_id=request.POST.get('transmitter'),
transmitter=transmitter[0],
start_time=start_time,
end_time=end_time,
author=request.user)
@ -338,8 +354,7 @@ def observation_new(request):
if request.method == 'POST':
return observation_new_post(request)
satellites = Satellite.objects.filter(transmitters__alive=True) \
.filter(status='alive').distinct()
satellites = Satellite.objects.filter(status='alive')
obs_filter = {}
if request.method == 'GET':
@ -388,8 +403,7 @@ def prediction_windows(request):
min_horizon = request.POST.get('min_horizon', None)
overlapped = int(request.POST.get('overlapped', 0))
try:
sat = Satellite.objects.filter(transmitters__alive=True) \
.filter(status='alive').distinct().get(norad_cat_id=sat_id)
sat = Satellite.objects.filter(status='alive').get(norad_cat_id=sat_id)
except Satellite.DoesNotExist:
data = [{
'error': 'You should select a Satellite first.'
@ -404,13 +418,19 @@ def prediction_windows(request):
}]
return JsonResponse(data, safe=False)
try:
downlink = Transmitter.objects.get(uuid=transmitter).downlink_low
except Transmitter.DoesNotExist:
transmitter = get_transmitter_by_uuid(transmitter)
if transmitter is None:
data = [{
'error': 'You should select a Transmitter first.'
'error': 'Error in DB API connection.'
}]
return JsonResponse(data, safe=False)
elif len(transmitter) == 0:
data = [{
'error': 'You should select a valid Transmitter.'
}]
return JsonResponse(data, safe=False)
else:
downlink = transmitter[0]['downlink_low']
start_date = make_aware(datetime.strptime(start_date, '%Y-%m-%d %H:%M'), utc)
end_date = make_aware(datetime.strptime(end_date, '%Y-%m-%d %H:%M'), utc)
@ -627,16 +647,36 @@ def station_log(request, id):
@ajax_required
def scheduling_stations(request):
"""Returns json with stations on which user has permissions to schedule"""
transmitter = request.POST.get('transmitter', None)
try:
downlink = Transmitter.objects.get(uuid=transmitter).downlink_low
except Transmitter.DoesNotExist:
uuid = request.POST.get('transmitter', None)
if uuid is None:
data = [{
'error': 'You should select a Transmitter first.'
'error': 'You should select a Transmitter.'
}]
return JsonResponse(data, safe=False)
else:
transmitter = get_transmitter_by_uuid(uuid)
if transmitter is None:
data = [{
'error': 'Error in DB API connection.'
}]
return JsonResponse(data, safe=False)
elif len(transmitter) == 0:
data = [{
'error': 'You should select a Transmitter.'
}]
return JsonResponse(data, safe=False)
else:
downlink = transmitter[0]['downlink_low']
if downlink is None:
data = [{
'error': 'You should select a valid Transmitter.'
}]
return JsonResponse(data, safe=False)
stations = Station.objects.filter(status__gt=0)
available_stations = get_available_stations(stations, downlink, request.user)
import sys
sys.stdout.flush()
data = {
'stations': StationSerializer(available_stations, many=True).data,
}
@ -651,8 +691,7 @@ def pass_predictions(request, id):
unsupported_frequencies = request.GET.get('unsupported_frequencies', '0')
try:
satellites = Satellite.objects.filter(transmitters__alive=True) \
.filter(status='alive').distinct()
satellites = Satellite.objects.filter(status='alive')
except Satellite.DoesNotExist:
pass # we won't have any next passes to display
@ -668,65 +707,68 @@ def pass_predictions(request, id):
start_date = make_aware(datetime.utcnow(), utc)
end_date = make_aware(datetime.utcnow() +
timedelta(hours=settings.STATION_UPCOMING_END), utc)
observation_date_min_start = (
observation_min_start = (
datetime.utcnow() + timedelta(minutes=settings.OBSERVATION_DATE_MIN_START)
).strftime("%Y-%m-%d %H:%M:%S.%f")
for satellite in satellites:
# look for a match between transmitters from the satellite and
# ground station antenna frequency capabilities
if int(unsupported_frequencies) == 0:
frequency_supported = False
transmitters = Transmitter.objects.filter(satellite=satellite)
for gs_antenna in station.antenna.all():
all_transmitters = get_transmitters_by_status('active')
if all_transmitters is not None and len(all_transmitters) > 0:
for satellite in satellites:
# look for a match between transmitters from the satellite and
# ground station antenna frequency capabilities
if int(unsupported_frequencies) == 0:
transmitter_supported = False
norad_id = satellite.norad_cat_id
transmitters = [t for t in all_transmitters if t['norad_cat_id'] == norad_id]
for transmitter in transmitters:
if transmitter.downlink_low:
if (gs_antenna.frequency <=
transmitter.downlink_low <=
gs_antenna.frequency_max):
frequency_supported = True
if not frequency_supported:
for gs_antenna in station.antenna.all():
if downlink_low_is_in_range(gs_antenna, transmitter):
transmitter_supported = True
break
if transmitter_supported:
break
if not transmitter_supported:
continue
try:
tle = satellite.latest_tle.str_array
except (ValueError, AttributeError):
continue
try:
tle = satellite.latest_tle.str_array
except (ValueError, AttributeError):
continue
station_passes, station_windows = predict_available_observation_windows(station,
None,
2,
tle,
start_date,
end_date,
satellite)
station_passes, station_windows = predict_available_observation_windows(station,
None,
2,
tle,
start_date,
end_date,
satellite)
if station_windows:
for window in station_windows:
valid = window['start'] > observation_date_min_start and window['valid_duration']
window_start = datetime.strptime(window['start'], '%Y-%m-%d %H:%M:%S.%f')
window_end = datetime.strptime(window['end'], '%Y-%m-%d %H:%M:%S.%f')
sat_pass = {'name': str(satellite.name),
'id': str(satellite.id),
'success_rate': str(satellite.success_rate),
'unvetted_rate': str(satellite.unvetted_rate),
'bad_rate': str(satellite.bad_rate),
'data_count': str(satellite.data_count),
'good_count': str(satellite.good_count),
'bad_count': str(satellite.bad_count),
'unvetted_count': str(satellite.unvetted_count),
'norad_cat_id': str(satellite.norad_cat_id),
'tle1': window['tle1'],
'tle2': window['tle2'],
'tr': window_start, # Rise time
'azr': window['az_start'], # Rise Azimuth
'altt': window['elev_max'], # Max altitude
'ts': window_end, # Set time
'azs': window['az_end'], # Set azimuth
'valid': valid,
'overlapped': window['overlapped'],
'overlap_ratio': window['overlap_ratio']}
nextpasses.append(sat_pass)
if station_windows:
for window in station_windows:
valid = window['start'] > observation_min_start and window['valid_duration']
window_start = datetime.strptime(window['start'], '%Y-%m-%d %H:%M:%S.%f')
window_end = datetime.strptime(window['end'], '%Y-%m-%d %H:%M:%S.%f')
sat_pass = {'name': str(satellite.name),
'id': str(satellite.id),
'success_rate': str(satellite.success_rate),
'unvetted_rate': str(satellite.unvetted_rate),
'bad_rate': str(satellite.bad_rate),
'data_count': str(satellite.data_count),
'good_count': str(satellite.good_count),
'bad_count': str(satellite.bad_count),
'unvetted_count': str(satellite.unvetted_count),
'norad_cat_id': str(satellite.norad_cat_id),
'tle1': window['tle1'],
'tle2': window['tle2'],
'tr': window_start, # Rise time
'azr': window['az_start'], # Rise Azimuth
'altt': window['elev_max'], # Max altitude
'ts': window_end, # Set time
'azs': window['az_end'], # Set azimuth
'valid': valid,
'overlapped': window['overlapped'],
'overlap_ratio': window['overlap_ratio']}
nextpasses.append(sat_pass)
data = {
'id': id,
@ -784,20 +826,13 @@ def station_delete(request, id):
return redirect(reverse('users:view_user', kwargs={'username': me}))
class TransmittersSerializer(serializers.ModelSerializer):
mode = serializers.SerializerMethodField()
class Meta:
model = Transmitter
fields = ('uuid', 'description', 'alive', 'downlink_low', 'mode',
'success_rate', 'bad_rate', 'unvetted_rate', 'good_count',
'bad_count', 'unvetted_count', 'data_count')
def get_mode(self, obj):
if obj.mode is None:
return "No Mode"
return obj.mode.name
def transmitters_with_stats(transmitters_list):
transmitters_with_stats_list = []
for transmitter in transmitters_list:
transmitter_stats = transmitter_stats_by_uuid(transmitter['uuid'])
transmitter_with_stats = dict(transmitter, **transmitter_stats)
transmitters_with_stats_list.append(transmitter_with_stats)
return transmitters_with_stats_list
def satellite_view(request, id):
@ -809,7 +844,12 @@ def satellite_view(request, id):
}
return JsonResponse(data, safe=False)
transmitters = Transmitter.objects.filter(satellite=sat)
transmitters = get_transmitters_by_norad_id(norad_id=id)
if transmitters is None:
data = {
'error': 'Error in DB API connection.'
}
return JsonResponse(data, safe=False)
data = {
'id': id,
@ -822,37 +862,47 @@ def satellite_view(request, id):
'unvetted_count': sat.unvetted_count,
'data_count': sat.data_count,
'future_count': sat.future_count,
'transmitters': TransmittersSerializer(transmitters, many=True).data,
'transmitters': transmitters_with_stats(transmitters)
}
return JsonResponse(data, safe=False)
def transmitters_view(request):
sat_id = request.POST['satellite']
norad_id = request.POST['satellite']
station_id = request.POST.get('station_id', None)
try:
sat = Satellite.objects.get(norad_cat_id=sat_id)
Satellite.objects.get(norad_cat_id=norad_id)
except Satellite.DoesNotExist:
data = {
'error': 'Unable to find that satellite.'
}
return JsonResponse(data, safe=False)
transmitters = Transmitter.objects.filter(satellite=sat, alive=True,
downlink_low__isnull=False)
transmitters_filtered = Transmitter.objects.none()
transmitters = get_transmitters_by_norad_id(norad_id)
if transmitters is None:
data = {
'error': 'Error in DB API connection.'
}
return JsonResponse(data, safe=False)
transmitters = [t for t in transmitters
if t['status'] == 'active' and t['downlink_low'] is not None]
if station_id:
supported_transmitters = []
station = Station.objects.get(id=station_id)
for gs_antenna in station.antenna.all():
transmitters_filtered = transmitters_filtered | transmitters.filter(
downlink_low__range=(gs_antenna.frequency, gs_antenna.frequency_max))
data = {
'transmitters': TransmittersSerializer(transmitters_filtered, many=True).data,
}
else:
data = {
'transmitters': TransmittersSerializer(transmitters, many=True).data,
}
for transmitter in transmitters:
transmitter_supported = False
for gs_antenna in station.antenna.all():
if downlink_low_is_in_range(gs_antenna, transmitter):
transmitter_supported = True
break
if transmitter_supported:
supported_transmitters.append(transmitter)
transmitters = supported_transmitters
data = {
'transmitters': transmitters_with_stats(transmitters)
}
return JsonResponse(data, safe=False)

View File

@ -34,10 +34,10 @@ $(document).ready(function() {
var good_percentage = 0;
var unvetted_percentage = 0;
var bad_percentage = 0;
if(transmitter.data_count > 0){
good_percentage = Math.round((transmitter.good_count / transmitter.data_count) * 100);
unvetted_percentage = Math.round((transmitter.unvetted_count / transmitter.data_count) * 100);
bad_percentage = Math.round((transmitter.bad_count / transmitter.data_count) * 100);
if(transmitter.total_count > 0){
good_percentage = Math.round((transmitter.good_count / transmitter.total_count) * 100);
unvetted_percentage = Math.round((transmitter.unvetted_count / transmitter.total_count) * 100);
bad_percentage = Math.round((transmitter.bad_count / transmitter.total_count) * 100);
}
modal.find('#transmitters').append(`
<div class="col-md-12 transmitter">
@ -48,7 +48,7 @@ $(document).ready(function() {
<div class="panel-body">
<span class="label label-default">Observations</span>
<span class="front-data-big">
<span class="transmitter-total-obs">` + transmitter.data_count + `</span>
<span class="transmitter-total-obs">` + transmitter.total_count + `</span>
<div class="progress pull-right">
<div class="progress-bar progress-bar-success transmitter-good"
data-toggle="tooltip" data-placement="bottom"

View File

@ -136,19 +136,19 @@
<div class="front-line">
<span class="label label-default">Transmitter</span>
<span class="front-data">
{{ observation.transmitter.description }}
{{ observation.transmitter_description }}
</span>
</div>
<div class="front-line">
<span class="label label-default">Frequency</span>
<span class="front-data">
{{ observation.transmitter.downlink_low|frq }}
{{ observation.transmitter_downlink_low|frq }}
</span>
</div>
<div class="front-line">
<span class="label label-default">Encoding</span>
<span class="front-data">
{{ observation.transmitter.mode|default:"-" }}
{{ observation.transmitter_mode|default:"-" }}
</span>
</div>
<div class="front-line">
@ -360,7 +360,7 @@
<div class="well well-sm data-well">
<img src="{{ demoddata.payload_demod.url }}" alt="observation-{{ observation.pk }}" class="img-responsive">
</div>
{% elif observation.transmitter.mode == 'CW' %}
{% elif observation.transmitter_mode == 'CW' %}
<div class="well well-sm data-well">
{{ demoddata.display_payload }}
</div>

View File

@ -232,10 +232,10 @@
{{ observation.satellite.name }}
</a>
</td>
<td>{{ observation.transmitter.downlink_low|frq }}</td>
<td>{{ observation.transmitter_downlink_low|frq }}</td>
<td>
<span data-toggle="tooltip" data-placement="bottom" title="{{ observation.transmitter.description }}">
{{ observation.transmitter.mode|default:"-" }}
<span data-toggle="tooltip" data-placement="bottom" title="{{ observation.transmitter_description }}">
{{ observation.transmitter_mode|default:"-" }}
</span>
</td>
<td>

View File

@ -163,8 +163,8 @@
{{ observation.satellite.name }}
</a>
</td>
<td>{{ observation.transmitter.downlink_low|frq }}</td>
<td>{{ observation.transmitter.mode }}</td>
<td>{{ observation.transmitter_downlink_low|frq }}</td>
<td>{{ observation.transmitter_mode }}</td>
<td>
<span class="datetime-date">{{ observation.start|date:"Y-m-d" }}</span>
<span class="datetime-time">{{ observation.start|date:"H:i:s" }}</span><br>