Weitere Optionen
| Zeile 40: | Zeile 40: | ||
aufzurufen. Dieses Script gibt in dieser Reihenfolge folgende Werte auf stdout aus: | aufzurufen. Dieses Script gibt in dieser Reihenfolge folgende Werte auf stdout aus: | ||
timestamp ADC-Wert Azimut Höhe Stunde Deklination Controlertemperatur | timestamp ADC-Wert Azimut Höhe Stunde Deklination Controlertemperatur | ||
macros/acquire_ref | |||
erweitert die Ausgabe von macros/acquire um eine weitere Spalte | |||
per default wird hier 19 mal der feed gemessen und danach 1 mal den Referenzkanal | |||
Es gibt hier noch 3 wichtige (Stellungs)Parameter | |||
1. Averaging Anzahl default:100 | |||
2. Verhältnis Signalwerte Referenzwerte default :20 heisst 19 Signal 1 Referenz | |||
3. Gesamtzahl der auszugebenden Werte default :-1 (endlos) | |||
== Datenaufnahme == | == Datenaufnahme == | ||
Version vom 19. November 2010, 23:11 Uhr
Paralleles Auslesen von diversen Sensoren und Zusammenbauen der Daten
Rohdatenspeicherung
Soweit ich verstanden habe, besteht die Aufgabe darin, den AD-Wandler mit einer Frequenz im Bereich 1 kHz und die Koordinaten mit einer Frequenz im Bereich 10 Hz auszulesen. Evtl. kommen auch mal noch weitere Sensoren hinzu. Die Weiterverarbeitung soll in Form von n-Tupeln (Zeitstempel; AD-Wert; Koordinaten) erfolgen. Das Ganze soll auch unter Belastung des Rechners noch zuverlässig erfolgen. Die Speicherung der Rohdaten erfolgt dabei sinnvollerweise nicht als n-Tupel, sondern in einem Raster der Art:
mit
Die Koordinaten werden also nur aller k Zeitschritte auch wirklich ausgelesen und aufgeschrieben. Aus solch einem File kann für die Weiterverarbeitung wieder eine Liste mit kompletten n-Tupen erstellt werden. Dabei kann man die Koordinaten zwischen den Stützstellen interpolieren oder man lässt sie einfach konstant bis zum nächsten Messpunkt. Evtl. würde eine von beiden Varianten irgendwelche Statistiken stören. Die Rohdaten verursachen so weniger Last beim Schreiben und belegen weniger Speicherplatz als wenn immer ganze n-Tupel geschrieben werden müssten.
Roh-Datenformat
Die bei der Messung der integralen Signalintensität im 1420MHz-Band am Detektorausgang gesampelten Daten werden im AVR-ADC-Board zunächst in einem Ringpuffer einem gleitenden Mittel unterzogen. Jeweils ein über RS232 aquirierter Messwert ist also schon ein Mittel über eine (einstellbare) Puffergröße (16-32768) und somit auch mit einer Zeitkonstanten behaftet. Dieser Messwert soll zusammen mit einem Zeitstempel im ASCII-Format abgelegt werden. Zusätzlich werden die äquatorialen Koordinaten benötigt und eine Information, ob das Signal der Antenne oder dem Referenzkanal entstammt.
Enthalten sein müssen:
- Zeitstempel, Format: yyyy/MM/dd hh:mm:ss.s
- Äquatorialkoordinaten (RA/Dec), Format: HH.h DD.d
- Messwert in ADC-Counts, floating-point
- Schalterstatus Referenz (0 oder 1)
Daneben sollte ein weiterer, davon unabhängiger Prozess alle Sensorenwerte und andere periphere Daten in regelmäßigen Zeitabständen in einer Log-Datei speichern. Dazu gehören diverse Temperaturen, Spannungen, Ströme, andere Umweltsensoren und evtl. INDI-RT-spezifische Werte, wie Motorgeschwindigkeiten o.ä. aber auch Observablen des Steuerrechners, wie z.b. die aktuelle Präzision der ntp-gesteuerten Systemuhr. Die Identifikation und ggf. Korrelation zu den Hauptdaten erfolgt wieder durch Vergabe eines eindeutigen Zeitstempels.
Bsp.:
#date time temp1 temp2 windsensor IMot1 IMot2 U1 U2 U3 U4 ntp-err 2010/04/19 23:25:11.765235000 28.5 43.1 15 0 3.5 11.8 5.1 24.6 0.25 2.3
Beispiel für Aufruf datataker:
bin/datataker /dev/ttyS5
Obige Zeile konnekted mit dem ADC und gibt die Daten mit timestamp auf stdout aus.
Um den Zusammenhang zwischen gemessenen Intensitäten und Koordinaten auszugeben ist
macros/acquire
aufzurufen. Dieses Script gibt in dieser Reihenfolge folgende Werte auf stdout aus: timestamp ADC-Wert Azimut Höhe Stunde Deklination Controlertemperatur
macros/acquire_ref
erweitert die Ausgabe von macros/acquire um eine weitere Spalte
per default wird hier 19 mal der feed gemessen und danach 1 mal den Referenzkanal Es gibt hier noch 3 wichtige (Stellungs)Parameter 1. Averaging Anzahl default:100 2. Verhältnis Signalwerte Referenzwerte default :20 heisst 19 Signal 1 Referenz 3. Gesamtzahl der auszugebenden Werte default :-1 (endlos)
Datenaufnahme
Um eine möglichst jitterarme Datenaufnahme zu gewährleisten scheint es sinvoll, die einzelnen Sensoren in jeweils einen eigenen Prozess oder Thread zu packen. Dann kann der sich in aller Genauigkeit um seinen Sensor kümmern und wird hoffentlich vom Betriebssystem richtig mit Zeitscheiben versorgt. Dabei benötigt man allerdings eine gemeinsame Zeitbasis mit ausreichender Genauigkeit.
In Daten_Auswerten#Einzelwerte steht etwas von einem Ringpuffer der im DA vorhanden sei und dessen eigenständiger Messwertaufnahme in eben diesen Ringpuffer. Wie funktioniert das denn eigentlich?
Ich stelle mir den prinzipiellen Ablauf so vor:
- zentrale Instanz übergibt Details wie Samplefrequenz, ... und den Startzeitpunkt an einzelne Messprozesse.
- Messprozesse warten auf Startzeitpunkt und legen dann los
- Messprozesse messen vor sich hin, in persönlichen großen Ringpuffer.
- zentrale Instanz holt sich ab und an aus den Ringpuffern die Daten die sie haben will und schreibt sie in ein File
Verschiedene Prozesse
Die Kommunikation könnte man über Pipes, Fifos, Shared-Memory, Message-Queues, TCP (Sockets) machen, im Prinzip auch mit MPI.
- MPI ist Mist, weil man dann immer ein MPI-System braucht
- Message Queues sind im Prinzip cool, praktisch aber viel zu kurz und für zu kleine Datenmengen gedacht (10 Messages a 8kB ist Standard unter Linux). Dafür soll das halt sehr schnell sein (im L1-Cache bleiben etc).
- Pipes, Fifos ist halt ein bisschen fummelig. TCP keine Ahnung
- SHMem ist eigentlich ziemlich ok. Man muss natürlich von Hand synchronisieren. Das ist eigentlich auch kein Ding weiter. Wenn man einmal Zugriff auf den gemeinsamen Speicherbereich hat, sollte das nicht schwieriger sein als mit OpenMP (s.u.).
Verschiedene Threads
Nachteil ist, dass die einzelnen Ausleseprozesse nicht gegeneinander geschützt sind.
OpenMP
Dafür gibt es die nahezu triviale Variante mit OpenMP. Der geringe Aufwand könnte die eher theoretischen Nachteile durchaus wieder wettmachen, finde ich. Ich stelle mit das ungefähr so vor:
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>
#include <unistd.h>
using namespace std;
int showAction;
/* Das ist eine etwas missbräuchliche aber immer noch sehr einfache Verwendung
von OpenMP zur Arbeit mit Threads. Auf alle Fälle ist es schön
einfach. Evtl. müssen die Jobs das Lesen nicht in einer critical Section
machen aber es wird hier nichts schaden.
*/
int job1(void)
{
for(;;)
{
#pragma critical
int sAct=showAction;
if (sAct&1) cout << "1";
cout.flush();
usleep(5000); // nominell 5 ms
}
}
int job2(void)
{
for(;;)
{
#pragma critical
int sAct=showAction;
if (sAct&2) cout << "2";
cout.flush();
usleep(5000); // 5 ms
}
}
int job3(void)
{
for(;;)
{
#pragma critical
int sAct=showAction;
if (sAct&4) cout << "3";
cout.flush();
usleep(5000); // 5 ms
}
}
int main()
{
int nthreads, tid;
omp_set_nested(1);
/* Fork a team of threads with each thread having a private tid variable */
#pragma omp parallel private(tid) num_threads (4)
{
/* Obtain and print thread id */
tid = omp_get_thread_num();
printf("Hello World from thread = %d\n", tid);
switch (tid)
{
case 1: job1(); break;
case 2: job2(); break;
case 3: job3(); break;
}
/* Only master thread does this */
if (tid == 0)
{
nthreads = omp_get_num_threads();
printf("Number of threads = %d\n", nthreads);
}
for(;;) // master thread verteilt die aufgaben
{
#pragma critical
showAction=rand();
usleep(100000); // nominell 100 ms
}
} /* All threads join master thread and terminate */
}
Synchronisation
Mit den Pragmas von OpenMP kann man eine Synchronisation erreichen. Ein Thread wartet auf andere Threads, ich würde denken aktiv. Für Zwecke des Datensampelns sind die meisten Threads fast immer in Wartestellung. OpenMP bietet die Möglichkeit, Locks zu setzen. Darauf aufbauend könnte man eine sinnvolle Datenübergabe bauen und dazwischen die Threads schlafen legen. Das stelle ich mir so vor:
Systemtechnik und Zeitkonstanten
Kompendium der Usage
- Vergesslichkeit etc. zu bekämpfen ...
- Allanplot: root # .L allanplot.C++ # allenplot() ... tatsächlich mit e, nicht a
- Macros: (zB)
- <makro>.C (zuvor <file> einsetzen): # root -l <makro>.C
- oder # root -> .x <makro.C
- 2dfft siehe Changeset 497 for trunk
Verfahren bei zeitkonstanten Signalen
Vorbehaltlich einer grafischen Darstellung. Die Einzelschritte sind für eine rise time gerechnet, die eine Amplitude von 90% auf der Flanke ergibt [[1]]. Keine Totzeiten eingerechnet.
- analoge Tiefpassfilterung resp. Integration im Detektor:
- 400ns für einen Anstieg von 10% auf 90% gem. Datenblatt AD8307
- zeit- und wertdiskrete Tiefpassfilterung im Analog-Digital-Konverter (ADC):
- Der ADC sampelt mit 20kHz, d.h. der Puffer ist in 50ms einmal durchgeschoben, je nach Firmware kann sich ein Faktor 2 oder gar 4 zu diesem Wert ergeben. Also 10...200ms
- zeit- und wertdiskrete Tiefpassfilterung (resp. Integration) per Software (precision:double):
- (Beispiel). 50 Samples von 2,5 Dauer eines Timestamps, sind (hier) 75s
Die 0,9-Zeitkonstante ergibt sich hier zu . Es ist ersichtlich dass die Zeitkonstante via Suche nach dem rms-Minimum ganz überwiegend von der Software bestimmt wird.
- Der Schwenk zum nächsten Punkt dauert ebenfalls mehrere Sekunden, sind hier alles in allem 80s.
- Unterstellt man einmal, dass
- die 5° breite "ideale" Keule mit 3dB Abfall in 1/5-Schritten noch akzeptable Werte bringt (sind 1°) und
- ein 1h in RA / 15° in Dec grosses Feld interessiert, so wären
- für diesen Scan 5h anzusetzen.
- ideale Wetterbedingungen vorausgesetzt
- Abhängig von der Deklination der Quelle sind hier Zu- und Abschläge möglich. Horizontprobleme, Messungen unter 30° Elevation sind in der Regel wertlos oder nicht möglich.