uint16_t Array in uint8_t Array umsortieren



  • Hi

    Ich habe ein uint16_t rawBuffer array das Daten enthält. Diese Daten muss ich umsortieren und auch ein paar bytes entfernen und dann in ein neues Array schreiben.

    uint16_t rawBuffer[8] = {0x8000,0x0000,0x1113,0x1224,0x1200,0x09DE,0x1300,0x07B9);  // ist eigentlich viel größer
    

    Die Daten im rawBuffer sind eigentlich 32bit Werte mit fester Reihenfolge:

    Word        Word       Word        Word
    0x80000000, 0x11131224, 0x120009DE, 0x130007B9  = FRAME
    

    Das ganze ist ein FRAME. Und aus diesem FRAME muss ich:

    • 0x80000000 verwerfen
    • 0x11 verwerfen
    • 0x12 verwerfen
    • 0x13 verwerfen

    sodass im Grunde nur das übrigbleibt:

    13 12 24 00 09 DE 00 07 B9
    

    was ich dann in ein Bytearray schreibe.

    uint16_t framecntr = 0;
    uint16_t writecntr = 0;
    uint8_t writeBuffer[13824];
    while(1){
           for(int i=0;i<8;i++){
                if(rawBuffer[i] == 0x8000){
                    writeBuffer[writecntr] =   rawBuffer[i+2];
                    writeBuffer[writecntr+1] = rawBuffer[i+3]>>8;
                    writeBuffer[writecntr+2] = rawBuffer[i+3];
                    writeBuffer[writecntr+3] = rawBuffer[i+4];
                    writeBuffer[writecntr+4] = rawBuffer[i+5]>>8;
                    writeBuffer[writecntr+5] = rawBuffer[i+5];
                    writeBuffer[writecntr+6] = rawBuffer[i+6];
                    writeBuffer[writecntr+7] = rawBuffer[i+7]>>8;
                    writeBuffer[writecntr+8] = rawBuffer[i+7];
                    writecntr+=9;
                    framecntr++;
                }
            }
      if(writecntr >= 13824){
           break;
    }
    

    Meine Frage: geht das ganze auch irgendwie eleganter als ich es gelöst habe? Ich habe so ein Gefühl dass ich das nicht optimal mache, oder meint ihr das ist okay so.

    (Nebeninfo: das ganze passiert auf einem STM32F469 µC, die Daten kommen per DMA in den rawBuffer, werden sortiert und in den writeBuffer geschrieben bis dieser voll ist und werden dann als Binary auf eine SD-Karte geschrieben...)



  • @pauledd ist das der echte Code?
    Deine Schleifen sehen mir nicht so aus, als würden sie etwas sinnvolles tun.



  • @manni66 Nein, ich habs korrigiert. Das ist alles nur aufs nötigste gekürzt.


  • Mod

    Da bräuchte es noch ein paar Gegenfragen, die alle ein bisschen zusammenhängen:

    • Ist die 0x80000000 ein magischer Wert, der den Beginn eines Frames markiert? Oder kann 0x80000000 auch innerhalb eines Frames auftauchen?
    • Haben Frames immer gleiche Länge? Falls ja, ist es die hier gezeigte?
    • Soll validiert werden, ob ein Frame auch wirklich mit 0x80000000 anfängt?
    • Sind alle Frames direkt nacheinander? Sind in dem Buffer noch andere Daten?
    • Was ist 13824?


  • Haben die Frames immer eine feste Länge?
    Auf die Schnelle mit den vorgegebenen Werten:

    // vorher ggf. rawBuffer[0] und rawBuffer[1] checken, dann:
    for(int i=2; i<8; i+=2)
    {
      uint16_t high=rawBuffer[i];
      uint16_t low=rawBuffer[i+1];
     
      // in diesem Fall immer 3 uint8_t schreiben (ggf. noch Byteorder beachten):
      // LO von high schreiben: (uint8_t) high
      // HI von low schreiben: (uint8_t) (low>>8)
      // LO von low schreiben: (uint8_t) low
    }
    

    Schreiben würde ich auch immer mit dem Postinkrement, wobei natürlich die Größe des Ziels und die Schreibposition vor der Schleife gecheckt werden sollte.

    writeBuffer[writecntr++]= /* */;
    

    Wenn das Ziel ohnehin eine Datei ist, könntest du die Werte natürlich auch direkt ohne diesen Umweg schreiben.



  • @SeppJ sagte in uint16_t Array in uint8_t Array umsortieren:

    Da bräuchte es noch ein paar Gegenfragen, die alle ein bisschen zusammenhängen:

    • Ist die 0x80000000 ein magischer Wert, der den Beginn eines Frames markiert? Oder kann 0x80000000 auch innerhalb eines Frames auftauchen?

    ja genau, das ist der "Header", jeder Frame beginnt damit und bedeutet das die folgenden Daten gültig sind. 0x11, 0x22 und 0x33 ist nur ein Identifizierer um welche Daten es sich Handelt. Die Daten sind dann jeweils die restlichen 3 Byte im Word.

    • Haben Frames immer gleiche Länge?

    ja

    • Sind alle Frames direkt nacheinander? Sind in dem Buffer noch andere Daten?

    Keine weiteren Daten, die Frames kommen immer schön hintereinander 0x80..., 0x11..., 0x12.., 0x13...,

    EDIT: Also im rawBuffer sind auch ungültige Daten, deshalb prüfe ich wann 0x80000000 auftaucht welches mir dann garantiert das die nächsten 3 Words gültig sind.

    Der rawbuffer kann also auch so aussehen:

    0xC0000000 0x11421233 0x12123456 0x13232123 0x80000000 0x11323532 0x12235432 0x13235432 ...
    

    Da wäre alles vor 0x80000000 ungültig...

    • Was ist 13824?

    13824 ist das gemeinsammte Vielfache von 512B * 3 Words *9Bytes.
    Da ich immer ein vielfaches von 512B auf die SD-Karte schreiben muss und dabei immer ganze Frames (9Byte) haben will. In 13824B passen 512B genau 27 mal rein und ist dabei durch 3 Teilbar (die drei Datenwerte ich ich aussortiert habe).


  • Mod

    Ich würde es so in dieser Richtung machen. Das sollte dir genug Freiheit geben, eventuelle Anpassungen selber vorzunehmen, da ich jetzt nicht jedes Detail erfragt habe:

    #include <stdio.h>
    #include <stdint.h>
    
    
    const int WRITEBUFFERSIZE = 9*2;
    
    void write_buffer_to_ssd(uint8_t *begin, uint8_t *end)
    {
      for(int i=0; begin != end; ++i, ++begin)
        {
          printf("%.2x ", *begin);
          if (i%9 == 8) putchar('\n');
        }
    }
    
    
    static inline uint8_t high_byte(uint16_t word)
    {
      return (word & 0xFF00) >> 8;
    }
    
    
    static inline uint8_t low_byte(uint16_t word)
    {
      return word & 0x00FF;
    }
    
    
    uint8_t * transform_frame(uint16_t *frame_ptr, uint8_t *write_ptr)
    {
      // Hier optional prüfen, ob Frame mit 0x80000000 beginnt
      for(int i = 2; i < 8; i+=2)
        {
          // Hier optional prüfen, ob Markerbytes (high_byte(frame_ptr[i])) erlaubte Werte haben
          *write_ptr++ = low_byte(frame_ptr[i]);
          *write_ptr++ = high_byte(frame_ptr[i+1]);
          *write_ptr++ = low_byte(frame_ptr[i+1]);
        }
      return write_ptr;
    }
    
    
    int main()
    {
      uint16_t rawBuffer[] = {0x8000,0x0000,0x1113,0x1224,0x1200,0x09DE,0x1300,0x07B9,
                              0x8000,0x0000,0x1114,0x1325,0x1201,0x10DF,0x1301,0x08C0,
                              0x8000,0x0000,0x1112,0x1123,0x1299,0x07DD,0x1300,0x06B8};
      uint8_t writeBuffer[WRITEBUFFERSIZE];
      uint8_t *write_ptr = writeBuffer;
    
    
      for(size_t i = 0; i < sizeof(rawBuffer)/sizeof(*rawBuffer); i+=8)  // Hier anpassen, wie du über die echten Daten iterierst
        {
          write_ptr = transform_frame(rawBuffer + i, write_ptr);
          if (write_ptr - writeBuffer >= WRITEBUFFERSIZE)
            {
              write_buffer_to_ssd(writeBuffer, write_ptr);
              write_ptr = writeBuffer;
            }
        }
      write_buffer_to_ssd(writeBuffer, write_ptr);  // Schreibt eventuelle Reste
    }
    


  • Was mich hier etwas verwirrt ist der wilde Mix aus uint8_t, uint16_t und uint32_t
    Stimmt die Annahme, dass ein Frame aus einem 32bit Header mit dem Wert 0x80000000 besteht, dem eine Anzahl von Messdaten folgen, die selbst jeweils aus einem 32bit uint bestehen? Und jeder Messwert hat selbst einen Typ, der in die obersten 8bit kodiert ist, die unteren 24bit sind der Messwert selbst? Das würde ich dann auch so modellieren:

    struct DataPoint
    {
       std::uint32_t RawValue = 0;
    
       std::uint8_t type() const
       {
          return RawValue >> 24;
       }
    
       std::uint32_t value() const
       {
          return RawValue & 0xffffff;
       }
    };
    
    struct Frame
    {
       static const std::uint32_t ValidHeaderValue = 0x8000000;
    
       std::uint32_t Header = 0;
       std::vector<DataPoint> Data;
    
       bool valid() const
       {
          return Header = ValidHeaderValue;
       }
    }
    
    std::vector<uint8_t> process_frame( Frame const& frame )
    {
       std::vector<uint8_t> retval;
       if( frame.valid() )
       {
           for( auto const& dp : frame.Data )
           {
              if( dp.type() == 0x11 || dt.type() == 0x12 || dt.type() == 0x13 )
              {
                 // Funktion von LOBYTE/LOWORD/HIBYTE/HIWORD sollten klar sein, 
                 // hab hier einfach die Makros aus der WINAPI genommen
                 retval.push_back( LOBYTE( LOWORD( dt.value() ) );   
                 retval.push_back( HIBYTE( LOWORD( dt.value() ) );   
                 retval.push_back( LOBYTE( HIWORD( dt.value() ) );   
               }
           }
           return retval;
       }
    }
    
    

  • Mod

    @DocShoe : C-Forum, nicht C++
    Ansonsten Zustimmung, dass ich bei dem uint-Mix auch den Verdacht habe, dass in der Anforderung vielleicht wichtige Teile (unbeabsichtigt) fehlen oder Annahmen gemacht werden, die nicht unbedingt gelten.



  • @SeppJ
    Irre... ich schaffe es immer wieder relevante Details zu ignorieren 😕
    Trotzdem kann man das Datenmodell so aufbauen, man hat dann nur keinen vector zur Verfügung und muss dann halt in ein uint8_t Array schreiben.



  • @DocShoe sagte in uint16_t Array in uint8_t Array umsortieren:

    Was mich hier etwas verwirrt ist der wilde Mix aus uint8_t, uint16_t und uint32_t

    ALSO ... 🙂

    Die Daten kommen per SPI von einem EKG-Frontend, quasi ein 24-bit ADC (stark vereinfacht). Der STM32f469 der die Daten entgegen nimmt kann nur max. 16bit lesen und schreiben (8 kann er auch). Das heißt für ein 32bit Word wie 0x80000000 muss ich zwei 16bit Transfers machen und da ich das per DMA mache flutschen die Daten halt in ein uint16_t array.

    Das ich die Daten dann beim sortieren in ein uint8_t array packe um dieses dann an die SD-Karte zu senden ist evtl. ungeschickt. Vielleicht kann ich auch gleich ein uint16_t array auf die Karte schreiben.

    Ich kontempliere erstmal über eure Ansätze 🙂


  • Mod

    Unabhängig deiner Frage drängt sich der Verdacht auf, dass du vielleicht zu sehr vereinfacht hast, vielelicht unabsichtlich. Denn: Wieso sind da magische Frameheader drin, wenn die Frames immer die gleiche Länge haben? Wozu die Marker 11,12,13, wenn diese immer gleich sind?

    Bist du wirklich sicher, dass deine Annahmen richtig sind und diese Informationen getrost wegeworfen werden können? Denn wenn sie so unnötig wären, dann hätte man sie wahrscheinlich nicht eingebaut.



  • @pauledd sagte in uint16_t Array in uint8_t Array umsortieren:

    EKG-Frontend

    Wenn damit soetwas wie Frontend AD8232 für EKG-Herzfrequenzmesser gemeint ist, machen mir deine Fragen hier Angst.



  • @pauledd
    Du orientierst dein Design am Übetragungsweg, das sollte man mMn nicht machen. Überleg´ dir ein Datenmodell, das deine Daten vernünftig repräsentiert, ohne durch irgendwelche Protokolleigenschaften eingeschränkt zu sein. Anschließend braucht du mind. einen Protokollhandler, der deine Daten im protokollspezifischen Format lesen und schreiben kann. Die Fummelei auf Telegrammebene würde ich so nicht machen.

    PS:
    Schon deine Aufgabenbeschreibung ist falsch. Du willst nicht alle 0x11, 0x12, 0x13 aus den Daten entfernen. Deiner Beschreibung nach willst du alle 0x11, 0x12, 0x13 aus den Telegrammdaten entfernen, wenn sie die obersten 8 Bit eines DWORDs sind.



  • @SeppJ sagte in uint16_t Array in uint8_t Array umsortieren:

    Wieso sind da magische Frameheader drin, wenn die Frames immer die gleiche Länge haben? Wozu die Marker 11,12,13, wenn diese immer gleich sind?

    Die Header mit 0x80 zeigen an das der ADC die Daten fertig gesampled hat und ausgelesen werden können. 0x11 0x12 0x13 bezeichnen die jeweiligen EKG-Elektroden, nämlich LA, LL und RA. Die Anzahl und welche Elektroden ausgelesen werden sollen muss vor dem Transfer konfiguriert werden und für meine Anwendung reichen mir die drei (von fünf möglichen).

    Datenaufbau:
    http://p-bg.de/pics/Screenshot_20210505_142516.png

    Mitschnitt eines Transfers:
    http://p-bg.de/pics/Screenshot_20210505_143730.png

    Beschreibung der Daten Words:
    http://p-bg.de/pics/Screenshot_20210505_145510.png

    Aber das geht schon viel zu weit... die Daten kommen nun mal so an und ich will die Binary Datei so klein wie möglich halten. Ich habe zuvor immer die ganzen 32bit (0x11......., 0x12........0x1300000) in einer Binary gespeichert und das klappte soweit ganz gut.

    Also werfe ich den Word-Identifizierer und den Header weg und spare fast 500MB Daten wenn ich 24h Daten auf die SD-Karte logge...



  • @DocShoe sagte in uint16_t Array in uint8_t Array umsortieren:

    Die Fummelei auf Telegrammebene würde ich so nicht machen.

    Was meinst du mit Telegramm?



  • @manni66 sagte in uint16_t Array in uint8_t Array umsortieren:

    Wenn damit soetwas wie Frontend AD8232 für EKG-Herzfrequenzmesser gemeint ist, machen mir deine Fragen hier Angst.

    Keine Angst, das ganze wird nicht am echten Menschen getestet sondern nur simuliert 🙂


  • Mod

    @pauledd sagte in uint16_t Array in uint8_t Array umsortieren:

    Aber das geht schon viel zu weit... die Daten kommen nun mal so an und ich will die Binary Datei so klein wie möglich halten. Ich habe zuvor immer die ganzen 32bit (0x11......., 0x12........0x1300000) in einer Binary gespeichert und das klappte soweit ganz gut.

    Also werfe ich den Word-Identifizierer und den Header weg und spare fast 500MB Daten wenn ich 24h Daten auf die SD-Karte logge...

    Kompression?



  • @SeppJ sagte in uint16_t Array in uint8_t Array umsortieren:

    Kompression?

    Damit kenne ich mich absolut nicht aus und würde den Rahmen sprengen. Ich weiß auch nicht wieviel CPU Zeit Komprimierung beanspruchen würde da das ganze nicht Zeitunkritisch ist.

    Der Speicher auf dem µC ist schon fast gänzlich für den Lese/Schreibpuffer ausgereizt und vom Timing her würde ich ungern noch in irgendwelchen zusätzlichen Funktionen CPU Zeit investieren.

    Später ist evtl. noch geplant das ISHNE Binärformat für EKG Daten zu implementieren. Aber das ist nur eine Idee...


  • Mod

    Ich meinte das nicht so, dass du selber Kompressionsalgoithmen implementieren sollst. Sondern, dass du fertige Standardkompressionsprogramme nutzen solltest. Die verkleinern deine Daten nochmals deutlich mehr als das, was du dir hier selber ausdenkst, und du sparst dabei massiv deine eigene (nicht kostenlose) Zeit, und vermeidest zudem auch alle vorstellbaren Fehler.

    Aber wenn's wirklich ein Ressourcenbottleneck gibt, dann verwirf die Idee. Oder besser noch: Prüf es einmalig. Vielleicht verschätzt du dich ja. Wenn's nämlich doch reicht, dann gewinnst du viel.


Anmelden zum Antworten