Debuggaillaan ressun kelloketjuja

Tässä uusi debukkailuversio ressun kellon luvusta. Tämä lukee kelloa puskurillisen kerrallaan, tavallisen kellomerkki kerrallaan tavan sijasta. Jälkimmäinen tapa ei ole kovin mukava debukkailijan kannalta, koska kellojono tietysti etenee debukkaustulosteiden tulostamisen aikana ja kellojonot lyhenevät, joskus häviävät kokonaan. Kellomerkkejä voi olla vain yksi per kellon arvo.

Edit: Tehty korjauksia julkaisun jälkeisten viikkojen aikana. Lisäksi muotoilin ohjelman uudelleen siten että myös puuttuvat ketjut lisäävät tarvittaessa yhden bitin teoreettista satunnaisuutta. Muutokset ovat kappaleessa if(prevbyte != byte).

Tässä postissa on myös kellojonojen debukkailuun tarkoitettu versio ressu_genbytes() rutiinista. Se tulostaa teoreettisten satunnaisbittien laskentaan liittyviä tietoja.

Mutta ensin uusi versio kellon luennasta. Ohjelma lukee kellojonon merkit clock_bytes[] taulukkoon ja palauttaa merkkejä yhden kerrallaan. Uudessa luennassa yritetään uusi puskuri aloittaa edellisen puskurin viimeisellä arvolla (cb), jos tämä ei ole ensimmäinen puskuri (clock_byte!=99…). Clock_bytes[], clock_byte, clock_cnt ja niiden käyttö ovat tuttuja jo ressu_genbyte() rutiinista, toki ressu_ alkuisina kenttinä. Kun haluat lopettaa kellon debukkaamisen, lisää kommentti #define DEBUG_CLOCK lauseeseen tai lisää pikku-a sen DEBUG_CLOCK sanan ensimmäiseksi kirjaimeksi.

Kellojonot toki pitenevät tässä versiossa, koska ne talletetaan tässä nopeassa luupissa (for(int c=0…). Toisaalta luulisin että ilman debukkia kellojonojen pituuksissa on enemmän vaihtelua.

#define DEBUG_CLOCK 2

#ifdef DEBUG_CLOCK
#define CLOCKCNT 256

unsigned char clock_bytes[CLOCKCNT];
int clock_byte = 999999999;
int clock_cnt = CLOCKCNT;

unsigned char clockbyte() /* JariK 2020 */
{
  if(clock_byte >= clock_cnt) {
    struct timeval tv;
    if(clock_byte != 999999999) {
      unsigned char cb;
      gettimeofday(&tv, NULL);
      cb = tv.tv_usec & 0xff;
      while(cb != clock_bytes[clock_cnt-1]) {
        gettimeofday(&tv, NULL);
        cb = tv.tv_usec & 0xff;
      }
    }
    for(int c = 0; c < CLOCKCNT; c++) {
      gettimeofday(&tv, NULL);
      clock_bytes[c] = tv.tv_usec & 0xff;
    }
    clock_byte = 0;
  }
  return(clock_bytes[clock_byte++]);
}

#else //#ifdef DEBUG_CLOCK                                                                                                                                                                                  

unsigned char clockbyte() /* JariK 2013 */
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return(tv.tv_usec & 0xff);
}

#endif //#ifdef DEBUG_CLOCK

Sitten kellojonojen debukkailuun tarkoitettu versio ressu_genbytes() rutiinista: Raportin tulostaa kappale, joka alkaa tulostustiedoston avaamisella (fp1=fopen).

Toinen ero oikeaan versio 1.8 rutiiniin on tuo rndbits kentän kasvatus kahdella, eli yksi poikkeusjono on kaksi bittiä satunnaisuutta. Ajatus on että poikkeus olisi “ykkösbitti” ja sille voidaan laskea ylimääräinen “nollabitti” noista perusjonoista, joita yritetään olla laskematta. Tämä teoriassa lyhentää suoritusaikaa. Hmmmmm…

void ressu_genbytes(int size, unsigned char *buffer) /* JariK 2020 v1.8 */
{
  int c, d, e, f, g,
    byte, prevbyte,
    clockbytes = 0, rndbits = 0,
    chainbytes = 0, oldindex = 0, oldcount;
  unsigned char olddata[10];

  memset(olddata, 0, sizeof(olddata));
  prevbyte = clockbyte();
  f = 0;

  for(c = 0; c < 8 || c%8 != 0 || 
      clockbytes < 2000 ||
      rndbits < 8*size; c++) {

    for(d = 0; d < size; d++) {
      e = buffer[d];
      e = ((e&0x80)>>7) | ((e&0x7f)<<1);
      byte = clockbyte();
      buffer[d] = e^byte;

      if(prevbyte != byte) {
        oldcount = 0;
        for(g = 0; g < sizeof(olddata); g++) {
          if(olddata[g] == chainbytes)
            oldcount++;
        }

        int tbit, tbit2;
        tbit=0;
        tbit2=0;
        if(oldcount < 4) {
          tbit=1;
          rndbits+=2;                                                                                                                            
        } else if((prevbyte+1)%256 != byte) {
          tbit2=1;
          rndbits++;
        }

        FILE *fp1;
        if((fp1 = fopen("ressuchains.deb",
            "a")) != NULL) {
          fprintf(fp1,"id=%d", id);
          fprintf(fp1,", c=%3d", c);
          fprintf(fp1,", c%%8=%d", c%8);
          fprintf(fp1,", prevbyte=%3d",
              prevbyte);
          fprintf(fp1,", byte=%3d",
              byte);
          fprintf(fp1,", tbits=%4d",
              rndbits);
          fprintf(fp1,", length=%2d",
              chainbytes);
          fprintf(fp1,", tbit=%d", tbit);
          fprintf(fp1,", tbit2=%d", tbit2);
          fprintf(fp1,", oldcount=%d",
              oldcount);
          fprintf(fp1,", data=");
          for(g = 0; g < sizeof(olddata); g++)
            fprintf(fp1," %02d",
                olddata[g]);
          fprintf(fp1,"\n");
          fclose(fp1);
          id++;
        } // if((fp1

        olddata[oldindex] = chainbytes;
        oldindex = (oldindex + 1) %
            sizeof(olddata);

        clockbytes += chainbytes;
        chainbytes = 0;

        prevbyte = byte;
      } // if(prevbyte != byte)
      chainbytes++;
    } // for(d = 0;

    for(d = 0; d < size; d++) {
      f = (f + buffer[d]) % size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    } // for(d = 0
  } // for(c = 0
}

Edellisen rutiinin tuloste: Tämä on ehkä vähän lukukelvoton tällä kapealla palstalla, mutta tässä näkyy kuitenkin tärkeimmät numerot, c:ssä on pääluupin (for(c=0) kierroksen numero, c%8 kertoo käsiteltävän bitin numeron välillä 0-7, byte kertoo kellomerkin arvon, tbits kertoo teoreettisten bittien lukumäärän, length taas tämän kellojonon pituuden, tbit kertoo onko tällä rivillä ollut teoreettinen satunnaisbitti vai ei, oldcount antaa tämän pituuden esiintymien lukumäärän. Data kentässä on kaikkien kymmenen viimeisen kellojonon pituudet.

id=568, c= 52, c%8=4, byte=127, tbits= 552, length=23, tbit=1, oldcount=1, data= 49 26 24 26 24 26 22 26 23 51
id=569, c= 53, c%8=5, byte=128, tbits= 552, length=26, tbit=0, oldcount=5, data= 49 26 24 26 24 26 22 26 23 26
id=570, c= 53, c%8=5, byte=129, tbits= 554, length=27, tbit=1, oldcount=1, data= 27 26 24 26 24 26 22 26 23 26
id=571, c= 53, c%8=5, byte=130, tbits= 556, length=25, tbit=1, oldcount=1, data= 27 25 24 26 24 26 22 26 23 26
id=572, c= 53, c%8=5, byte=131, tbits= 558, length=23, tbit=1, oldcount=2, data= 27 25 23 26 24 26 22 26 23 26
id=573, c= 53, c%8=5, byte=132, tbits= 560, length=27, tbit=1, oldcount=2, data= 27 25 23 27 24 26 22 26 23 26
id=574, c= 54, c%8=6, byte=133, tbits= 560, length=26, tbit=0, oldcount=4, data= 27 25 23 27 26 26 22 26 23 26
id=575, c= 54, c%8=6, byte=134, tbits= 562, length=25, tbit=1, oldcount=2, data= 27 25 23 27 26 25 22 26 23 26

Vielä yksi sivuhuomautus: tässä ovat esiintyneet kaikki byte arvot eli kellon arvot välillä 127-134, mikä lienee yleisin tapaus. Voisi kuvitella että on tilanteita jossa ohitetaan yksi kelloarvo kokonaan, esimerkiksi hidas kone tms. Se voisi olla teoreettisen satunnaisbitin paikka, ainakin siinä tapauksessa ettei bittejä muuten saada laskettua. Jos esimerkiksi kaikki kellojonot on yhden merkin pituisia, meillä on data taulukko täynnä ykkösiä (10 kpl), jälleen uutta ykköstä ei tietenkään lasketa. Jos tässä tilanteessa olisi kellon arvoilla välejä saataisiin silti kasvatettua satunnaisbittejä. Teoreettinen huomautus, toivottavasti.

Sitten pieni pääohjelma

void main(int argc,char *argv[])
{
  int c;
  unsigned char buffer[128];

  FILE *fp1;
  if((fp1 = fopen("ressuchains.deb",
      "w")) != NULL)
    fclose(fp1);

  for(c = 0; c < 20; c++)
    ressu_genbytes(sizeof(buffer), buffer);

  for(c = 0; c < sizeof(buffer); c++) {
    if(c > 0 && c%32 == 0)
      fprintf(stdout,"\n");
    fprintf(stdout," %02x", buffer[c]);
  }
  fprintf(stdout,"\n");
}