1
0
Fork 0

Merge pull request #91 from satnogs/decoding-telemetry

Decoding telemetry data
pull/93/merge
Nikos Roussos 2016-05-08 18:46:53 +03:00
commit 8802acf7f0
17 changed files with 280 additions and 17 deletions

View File

@ -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']

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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')

View File

@ -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),
),
]

View File

@ -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()

View File

@ -1,4 +1,4 @@
from django.conf.urls import patterns, url
from django.conf.urls import url
from db.base import views

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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