Labor – Array-Verwaltung mit LEER_WERT

Ein Temperatursensor speichert in Abständen von 30 Minuten Messwerte der Außentemperatur. Eine gelegentlich auftretende Störung führt zu unbrauchbaren "Ausreißern". Zur Korrektur mit Methode removeIfBad(…​) wird jeder Wert mit seinen Nachbarwerten verglichen. Wenn er um mehr als 2°C vom Mittelwert seiner Nachbarwerte abweicht, wird er gelöscht.

Dabei wird angenommen, dass wegen Kürze und geringer Häufigkeit der Störungen praktisch nie 2 direkt hintereinander auftreten.

Bereinigungen werden erst NACH Abschließen der Messungen vorgenommen.
Erste und letzte Messung werden zu Beginn händisch mit printMesswerte(…​) kontrolliert und bei Bedarf mittels setFirst(…​), setLast(…​) korrigiert, sodass die Kontrolle vom 2. bis zum vorletzten Wert automatisch erfolgen kann.

Anschließend werden einige Auswertungen mit den Daten durchgeführt.

Die Daten liegen als byte-Werte vor (-128 bis 127), wobei der mögliche Temperaturbereich von -40°C bis +60°C liegt. Die Konstante Byte.MIN_VALUE ist daher ein guter LEER_WERT (als Konstante zu definieren).

TempSensorEMPTY_VAL: bytesensorData: byte[]dataCleaned: boolean = falseConstructor()Constructor(capacity: int)add(sensorValue : byte): booleanDatenerfassungremove(idx: int): boolean // false wenn idx ungültigsetFirst(sensorValue: byte)setLast(sensorValue: byte)removeIfBad(idx: int): char // siehe BeschreibungcleanData()BereinigungmaxUsedIdx(): intminValue(): bytemaxValue(): byteaverage(): floatAuswertungenprintSensorData(spalten: int)printDiagram(startIdx: int, endIdx: int)Ausgabe
  • EMPTY_VAL …​ Konstante mit niedrigstem Byte-Wert (Konstante verwenden, nicht Zahlenwert direkt!)

  • Konstruktor() …​ legt ein Array für 24 Stunden (48 Messwerte) an und initialisiert es mit EMPTY_VAL

  • Konstruktor(…​) …​ legt Array mit gegebener Kapazität an, (min. 6=3h, max 336=7*24*2=1Wo Werte)

  • add(sensorValue: byte): boolean …​ Datenerfassungsmethode (wird vom Sensor aufgerufen).
    Dabei wird das Array nach der ersten Zelle mit Inhalt EMPTY_VAL gesucht und der übergebene Wert darin gespeichert (damit ist die Zelle als genutzt erkennbar) und wird für den nächsten Aufruf nicht mehr genutzt. Wenn keine freie Zelle mehr gefunden wird, ist der Rückgabewert false, sonst true.
    Bei Verwendung nach cleanData() wird nichts hinzugefügt und false zurückgeliefert, da sonst neue Werte in eventuell entstandene Lücken gesetzt würden!

  • remove(idx: int): boolean …​ ersetzt den Zell-Wert durch EMPTY_VAL. Falls davor schon EMPTY_VAL enthalten war oder ein ungültiger Index übergeben wurde, wird false zurückgeliefert, sonst true.
    Zusätzlich Fehlermeldung, wenn idx ungültig war.

  • setFirst(sensorValue: byte) …​ setzt die erste Zelle auf den übergebenen Wert.

  • setLast(sensorValue: byte) …​ setzt die letzte Zelle auf den übergebenen Wert.

  • removeIfBad(idx: int): char …​ prüft Wert in Zelle idx und entfernt ihn bei Bedarf.

    Die Prüfung erfolgt durch Vergleich mit den beiden Nachbar-Werten. Somit beginnt die Prüfung bei der zweiten und endet bei der vorletzten Zelle.
    Wenn idx gültig und sein "linker" und "rechter" Nachbar gültige Werte sind, wird der Durchschnitt der beiden Nachbarzell-Werte gebildet und mit dem Wert verglichen.
    Zellwerte sind gültig, wenn ihre Abweichung (nach oben und unten) von diesem Durchschnitt nicht größer als 2 ist.
    Wenn nicht beide Nachbarn da sind, wird der Wert als nicht prüfbar (wird verwendet) angesehen.
    Bei zu großer Abweichung wird der Zellwert auf EMPTY_VAL gesetzt.
    Es gibt 4 mögliche Rückgabewerte (daher nicht boolean, sondern char):

    • 'i' …​ idx ungültig, kein Zell-Wert wird verändert

    • '?' …​ eine oder beide Nachbarzellen haben EMPTY_VAL, Zell-Wert bleibt unverändert

    • '$' …​ Wert war ok, Zell-Wert bleibt unverändert

    • '_' …​ Wert war ungültig, musste entfernt (auf EMPTY_VAL gesetzt) werden

  • cleanData() …​ durchläuft für alle Daten zwischen erster und letzter Zelle (diese sind nicht dabei) und wendet removeIfBad(..) an. Es wird auf den Bildschirm protokolliert mit Index, Rückgabewert ('i', '?', '$', '_'): und finaler Zell-Wert.
    Am Ende wird dataCleaned auf true gesetzt.

  • maxUsedIdx(): int …​ liefert den Index der letzten genutzten Zelle

  • minValue(): byte …​ kleinster Wert aller Zellen. + Implementierungsmöglichkeit: In einer Hilfsvariabeln (z.B. aktuellMin) wird zu Beginn der "gegenüberliegende Extremwert des Datentyps" (hier Byte.MAX_VALUE) gespeichert und dann jede Zelle damit verglichen. Falls ihr wert kleiner, wird er in aktuellMin gespeichert und dient ab nun als Vergleichsbasis. Am Ende wird dieser Wert zurückgeliefert.

  • maxValue(): byte …​ größter Wert aller Zellen (analog zu minWert(…​) )

  • average(): float …​ MittelWert – Summe aller gültigen Werte geteilt durch deren Anzahl

  • printSensorData(columns: int) …​ mehrspaltige Ausgabe der Messwerte. Siehe dazu z.B. Arrays-Intro: Kapazitätserhöhung, Methode printArray(…​).
    Für spalten=4 sollte bei 9 Messwerten z.B. folgende Ausgabe erzeugt werden:

Anzahl Messwerte: 9
-5, -3, -1, 1,
2, 4, 5, 7,
8
  • printDiagram(startIdx: int, endIdx: int) …​ Optionale Bonus-Aufgabe!
    "zeichnet" ein Diagramm (Genauigkeit 5°/Zeile) wie folgt:

 °C ^
 +60|
    |
 +50|
    |
 +40|
    |
 +30|
    |                     **** ********** **
 +20|             ** *****                  ***** ***
    |        *****
 +10|      **
    |   **
  0 +--*---------------------------------------------> Zeit (Einheit 1/2h)
    | *
 -10|*
    |
 -20|
    |
 -30|
    |
 -40|

Zur Lösung kann man folgendermaßen vorgehen:

  • Es muss zeilenweise von oben nach unten ausgegeben werden.

  • Die erste Zeile hat den Inhalt " °C ^"

  • Jede reguläre Zeile repräsentiert ein 5°-Intervall, das von 2° oberhalb bis 3° unterhalb des Zeilen-Nennwerts reicht. Die Nenn-Werte laufen von 60, 55, 50, 45, …​ -30, -35, 40.

  • Die regulären Zeilen alternieren bezüglich der Achsenbeschriftung.

  • Jede reguläre Zeile beginnt mit der Achsen-Ausgabe:

  • Wenn Zeilen-Nennwert durch 10 teilbar und > 0, wird das " +" gefolgt vom Zahlenwert gefolgt von "|" ausgegeben

  • bei negativen Temperaturen analog (" -" statt " +")

  • Die 0°-Zeile erfordert Sonderbehandlung (siehe Beispiel-Diagramm).

  • anderenfalls wird nur " |" ausgegeben

  • danach folgt für jede Zeile der "Daten-Teil":

  • Für jede Zeile wird der beim Methodenaufruf festgelegte Index/Zeit-Bereich an Array-Werten durchlaufen. Falls der jeweilige Array-Wert im aktuellen 5°-Intervall liegt, wird ein '*' ausgegeben, anderenfalls ein ' ' (bei der 0°-Zeile statt ' ' ein '-').

  • ungültige Werte liegen naturgemäß nicht im aktuellen Werte-Intervall, es wird also ebenfalls ' ' ausgegeben.