commit
8802acf7f0
|
@ -1,9 +1,18 @@
|
|||
import django_filters
|
||||
|
||||
from db.base.models import Transmitter
|
||||
from db.base.models import Transmitter, DemodData
|
||||
|
||||
|
||||
class TransmitterViewFilter(django_filters.FilterSet):
|
||||
class Meta:
|
||||
model = Transmitter
|
||||
fields = ['mode', 'satellite__norad_cat_id']
|
||||
|
||||
|
||||
class TelemetryViewFilter(django_filters.FilterSet):
|
||||
satellite = django_filters.NumberFilter(name='transmitter__satellite__norad_cat_id',
|
||||
lookup_type='exact')
|
||||
|
||||
class Meta:
|
||||
model = DemodData
|
||||
fields = ['satellite']
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from db.base.models import Mode, Satellite, Transmitter
|
||||
from db.base.models import Mode, Satellite, Transmitter, DemodData
|
||||
|
||||
|
||||
class ModeSerializer(serializers.ModelSerializer):
|
||||
|
@ -33,3 +33,26 @@ class TransmitterSerializer(serializers.ModelSerializer):
|
|||
|
||||
def get_norad_cat_id(self, obj):
|
||||
return obj.satellite.norad_cat_id
|
||||
|
||||
|
||||
class TelemetrySerializer(serializers.ModelSerializer):
|
||||
norad_cat_id = serializers.SerializerMethodField()
|
||||
transmitter = serializers.SerializerMethodField()
|
||||
appendix = serializers.SerializerMethodField()
|
||||
telemetry = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = DemodData
|
||||
fields = ('norad_cat_id', 'transmitter', 'appendix', 'telemetry')
|
||||
|
||||
def get_norad_cat_id(self, obj):
|
||||
return obj.transmitter.satellite.norad_cat_id
|
||||
|
||||
def get_transmitter(self, obj):
|
||||
return obj.transmitter.uuid
|
||||
|
||||
def get_appendix(self, obj):
|
||||
return obj.transmitter.satellite.telemetry_schema
|
||||
|
||||
def get_telemetry(self, obj):
|
||||
return obj.payload
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from django.conf.urls import patterns, url, include
|
||||
from rest_framework import routers
|
||||
|
||||
from db.api import views
|
||||
|
@ -9,5 +8,6 @@ router = routers.DefaultRouter()
|
|||
router.register(r'modes', views.ModeView)
|
||||
router.register(r'satellites', views.SatelliteView)
|
||||
router.register(r'transmitters', views.TransmitterView)
|
||||
router.register(r'telemetry', views.TelemetryView)
|
||||
|
||||
api_urlpatterns = router.urls
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from rest_framework import viewsets
|
||||
|
||||
from db.api import serializers, filters
|
||||
from db.base.models import Mode, Satellite, Transmitter
|
||||
from db.base.models import Mode, Satellite, Transmitter, DemodData
|
||||
|
||||
|
||||
class ModeView(viewsets.ReadOnlyModelViewSet):
|
||||
|
@ -20,3 +20,9 @@ class TransmitterView(viewsets.ReadOnlyModelViewSet):
|
|||
serializer_class = serializers.TransmitterSerializer
|
||||
filter_class = filters.TransmitterViewFilter
|
||||
lookup_field = 'uuid'
|
||||
|
||||
|
||||
class TelemetryView(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = DemodData.objects.all()
|
||||
serializer_class = serializers.TelemetrySerializer
|
||||
filter_class = filters.TelemetryViewFilter
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.template.loader import render_to_string
|
|||
from django.conf import settings
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
|
||||
from db.base.models import Mode, Satellite, Transmitter, Suggestion
|
||||
from db.base.models import Mode, Satellite, Transmitter, Suggestion, DemodData
|
||||
|
||||
logger = logging.getLogger('db')
|
||||
|
||||
|
@ -47,9 +47,9 @@ class SuggestionAdmin(admin.ModelAdmin):
|
|||
transmitter.update_from_suggestion(obj)
|
||||
obj.delete()
|
||||
except (Transmitter.DoesNotExist, AttributeError):
|
||||
obj.approved=True
|
||||
obj.citation=''
|
||||
obj.user=None
|
||||
obj.approved = True
|
||||
obj.citation = ''
|
||||
obj.user = None
|
||||
obj.save()
|
||||
|
||||
# Notify user
|
||||
|
@ -95,3 +95,13 @@ class SuggestionAdmin(admin.ModelAdmin):
|
|||
else:
|
||||
return '-'
|
||||
transmitter_data.allow_tags = True
|
||||
|
||||
|
||||
@admin.register(DemodData)
|
||||
class DemodDataAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'data_id', 'satellite')
|
||||
search_fields = ('data_id', 'transmitter')
|
||||
readonly_fields = ('data_id', 'transmitter', 'payload')
|
||||
|
||||
def satellite(self, obj):
|
||||
return obj.transmitter.satellite
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env python
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
EPOCH_DATE = datetime.strptime('20000101T000000Z', '%Y%m%dT%H%M%SZ')
|
||||
|
||||
|
||||
def decode_payload(payload, observation_datetime, data_id):
|
||||
dt = payload[:32]
|
||||
datasets = payload[32:]
|
||||
|
||||
# calculate initial datetime
|
||||
seconds = int(dt[24:] + dt[16:24] + dt[8:16] + dt[:8], 2)
|
||||
|
||||
telemetry = []
|
||||
|
||||
while datasets:
|
||||
dataset = datasets[:57]
|
||||
datasets = datasets[57:]
|
||||
|
||||
dataset_datetime = EPOCH_DATE + timedelta(seconds=seconds)
|
||||
seconds += 60
|
||||
satellite_datetime = datetime.strftime(dataset_datetime, '%Y%m%dT%H%M%SZ')
|
||||
|
||||
# mode
|
||||
status = dataset[0]
|
||||
|
||||
# battery voltage
|
||||
u = float(int(dataset[1:9], 2))
|
||||
bat_v = round((u + 60) / 20, 2)
|
||||
|
||||
# battery current
|
||||
u = float(int(dataset[9:17], 2))
|
||||
bat_c = round((u - 127) / 127, 2)
|
||||
|
||||
# 3v3 current
|
||||
u = float(int(dataset[17:25], 2))
|
||||
v3_c = round(u / 40, 2)
|
||||
|
||||
# 5v current
|
||||
u = float(int(dataset[25:33], 2))
|
||||
v5_c = round(u / 40, 2)
|
||||
|
||||
# temperature comms
|
||||
u = float(int(dataset[33:41], 2))
|
||||
comms_t = round((u - 60) / 4, 2)
|
||||
|
||||
# temperature eps
|
||||
u = float(int(dataset[41:49], 2))
|
||||
eps_t = round((u - 60) / 4, 2)
|
||||
|
||||
# temperature battery
|
||||
u = float(int(dataset[49:], 2))
|
||||
batt_t = round((u - 60) / 4, 2)
|
||||
|
||||
data = {
|
||||
'satellite_datetime': satellite_datetime,
|
||||
'observation_datetime': observation_datetime,
|
||||
'data_id': data_id,
|
||||
'damod_data': {
|
||||
'status': status,
|
||||
'bat_v': bat_v,
|
||||
'bat_c': bat_c,
|
||||
'v3_c': v3_c,
|
||||
'v5_c': v5_c,
|
||||
'comms_t': comms_t,
|
||||
'eps_t': eps_t,
|
||||
'batt_t': batt_t
|
||||
}
|
||||
}
|
||||
|
||||
telemetry.append(data)
|
||||
return telemetry
|
|
@ -3,7 +3,50 @@
|
|||
"fields": {
|
||||
"norad_cat_id": 7530,
|
||||
"name": "OSCAR 7 (AO-7)",
|
||||
"names": ""
|
||||
"names": "",
|
||||
"telemetry_schema": [
|
||||
{
|
||||
"key": "status",
|
||||
"description": "Status",
|
||||
"unit": "Boolean"
|
||||
},
|
||||
{
|
||||
"key": "bat_v",
|
||||
"description": "Battery Voltage",
|
||||
"unit": "Volt"
|
||||
},
|
||||
{
|
||||
"key": "bat_c",
|
||||
"description": "Battery Current",
|
||||
"unit": "Ampere"
|
||||
},
|
||||
{
|
||||
"key": "v3_c",
|
||||
"description": "3V3 bus current",
|
||||
"unit": "Ampere"
|
||||
},
|
||||
{
|
||||
"key": "v5_c",
|
||||
"description": "5V bus current",
|
||||
"unit": "Ampere"
|
||||
},
|
||||
{
|
||||
"key": "comm_t",
|
||||
"description": "COMM Temperature",
|
||||
"unit": "Celsius"
|
||||
},
|
||||
{
|
||||
"key": "eps_t",
|
||||
"description": "EPS Temperature",
|
||||
"unit": "Celsius"
|
||||
},
|
||||
{
|
||||
"key": "batt_t",
|
||||
"description": "Battery Temperature",
|
||||
"unit": "Celsius"
|
||||
}
|
||||
],
|
||||
"telemetry_decoder": "qb50"
|
||||
},
|
||||
"model": "base.satellite",
|
||||
"pk": 1
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.conf import settings
|
||||
|
||||
from db.base.models import Satellite, Transmitter, DemodData
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Fetch and decode Satellite data from Network'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
apiurl = settings.NETWORK_API_ENDPOINT
|
||||
data_url = "{0}data".format(apiurl)
|
||||
start_date = datetime.utcnow() - timedelta(days=int(settings.DATA_FETCH_DAYS))
|
||||
start_date = datetime.strftime(start_date, '%Y-%m-%dT%H:%M:%SZ')
|
||||
params = {'start': start_date}
|
||||
response = requests.get(data_url, params=params)
|
||||
|
||||
satellites = Satellite.objects.exclude(telemetry_decoder__exact='')
|
||||
|
||||
for obj in response.json():
|
||||
norad_cat_id = obj['norad_cat_id']
|
||||
data_id = obj['id']
|
||||
try:
|
||||
satellite = satellites.get(norad_cat_id=norad_cat_id)
|
||||
except Satellite.DoesNotExist:
|
||||
continue
|
||||
try:
|
||||
transmitter = Transmitter.objects.get(uuid=obj['transmitter'])
|
||||
except Transmitter.DoesNotExist:
|
||||
continue
|
||||
demoddata = DemodData.objects.filter(data_id=data_id).delete()
|
||||
|
||||
decoder_module = 'db.base.decoders.{0}'.format(satellite.telemetry_decoder)
|
||||
decoder = __import__(decoder_module, fromlist='.')
|
||||
|
||||
for demoddata in obj['demoddata']:
|
||||
payload_url = demoddata['payload_demod']
|
||||
observation_datetime = payload_url.split('/')[-1]
|
||||
payload = str(requests.get(payload_url).json())
|
||||
telemetry = decoder.decode_payload(payload, observation_datetime, data_id)
|
||||
for item in telemetry:
|
||||
DemodData.objects.create(payload=item, transmitter=transmitter,
|
||||
data_id=data_id)
|
|
@ -7,12 +7,15 @@ class Command(BaseCommand):
|
|||
|
||||
def handle(self, *args, **options):
|
||||
# Migrate
|
||||
self.stdout.write("Creating database...")
|
||||
call_command('migrate')
|
||||
|
||||
# Initial data
|
||||
self.stdout.write("Creating fixtures...")
|
||||
call_command('loaddata', 'modes')
|
||||
call_command('loaddata', 'satellites')
|
||||
call_command('loaddata', 'transmitters')
|
||||
|
||||
# Create superuser
|
||||
self.stdout.write("Creating a superuser...")
|
||||
call_command('createsuperuser')
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-05-04 21:04
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import jsonfield.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0002_auto_20150908_2054'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DemodData',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('data_id', models.PositiveIntegerField()),
|
||||
('payload', jsonfield.fields.JSONField(default=dict)),
|
||||
('transmitter', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.Transmitter')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='satellite',
|
||||
name='telemetry_decoder',
|
||||
field=models.CharField(blank=True, max_length=20),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='satellite',
|
||||
name='telemetry_schema',
|
||||
field=jsonfield.fields.JSONField(blank=True, default=dict),
|
||||
),
|
||||
]
|
|
@ -1,3 +1,4 @@
|
|||
from jsonfield import JSONField
|
||||
from shortuuidfield import ShortUUIDField
|
||||
|
||||
from django.core.validators import MinValueValidator
|
||||
|
@ -30,6 +31,8 @@ class Satellite(models.Model):
|
|||
names = models.TextField(blank=True)
|
||||
image = models.ImageField(upload_to='satellites', blank=True,
|
||||
help_text='Ideally: 250x250')
|
||||
telemetry_schema = JSONField(blank=True)
|
||||
telemetry_decoder = models.CharField(max_length=20, blank=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ["name"]
|
||||
|
@ -95,3 +98,9 @@ class Suggestion(Transmitter):
|
|||
|
||||
def __unicode__(self):
|
||||
return self.description
|
||||
|
||||
|
||||
class DemodData(models.Model):
|
||||
transmitter = models.ForeignKey(Transmitter)
|
||||
data_id = models.PositiveIntegerField()
|
||||
payload = JSONField()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from django.conf.urls import patterns, url
|
||||
from django.conf.urls import url
|
||||
|
||||
from db.base import views
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
from os import path, getenv
|
||||
import dj_database_url
|
||||
|
||||
BASE_DIR = path.dirname(path.dirname(__file__))
|
||||
|
||||
# Apps
|
||||
|
@ -182,6 +184,9 @@ REST_FRAMEWORK = {
|
|||
SECRET_KEY = getenv('SECRET_KEY', 'changeme')
|
||||
|
||||
# Database
|
||||
import dj_database_url
|
||||
DATABASE_URL = getenv('DATABASE_URL', 'sqlite:///db.sqlite3')
|
||||
DATABASES = {'default': dj_database_url.parse(DATABASE_URL)}
|
||||
|
||||
# NETWORK API
|
||||
NETWORK_API_ENDPOINT = getenv('NETWORK_API_ENDPOINT', 'https://network.satnogs.org/api/')
|
||||
DATA_FETCH_DAYS = getenv('DATA_FETCH_DAYS', 10)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from django.conf.urls import url, include
|
||||
from django.conf import settings
|
||||
from django.conf.urls import patterns, include, url
|
||||
from django.contrib import admin
|
||||
from django.views.static import serve
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
# Basic stuff
|
||||
django==1.9.5
|
||||
django==1.9.6
|
||||
django-shortuuidfield==0.1.3
|
||||
django-compressor==2.0
|
||||
django-jsonfield==0.9.19
|
||||
|
||||
# Configuration
|
||||
django-dotenv==1.4.1
|
||||
dj-database-url==0.4.0
|
||||
dj-database-url==0.4.1
|
||||
opbeat==3.3
|
||||
|
||||
# Security
|
||||
|
@ -22,7 +23,7 @@ Pillow==3.2.0
|
|||
# API
|
||||
djangorestframework==3.3.3
|
||||
markdown==2.6.6
|
||||
git+https://github.com/alex/django-filter.git@0.12.0#egg=django-filter
|
||||
django-filter==0.13.0
|
||||
|
||||
# Astronomy
|
||||
git+https://github.com/comzeradd/orbit.git@cache##egg=orbit
|
||||
git+https://github.com/comzeradd/orbit.git@cache#egg=orbit
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
# Debug & Testing
|
||||
django-debug-toolbar==1.4
|
||||
sqlparse==0.1.19
|
||||
factory-boy==2.6.1
|
||||
factory-boy==2.7.0
|
||||
flake8==2.5.4
|
||||
|
|
Loading…
Reference in New Issue