So ist es allerdings laut http://dragon32.info/info/basicfmt.html spezifiziert.
Dennoch ist IMHO das unnötig. Man hat die Block länge und zusätzlich das Ende markiert. Naja... Sind ja nur zwei Byte unnötige Zeichen...
EDIT: Generell muß ich mir nochmal alle Dokumente von http://dragon32.info/info/ genauer ansehen...
Dragon 32 Homecomputer Kassetten in ASCII umwandeln...
@jens: Es können, auch wenn man sich an diese Spezifikation hält, *innerhalb* eines BASIC-Programms drei Nullbytes vorkommen. Davon abgesehen halten die Leute sich nicht immer an Spezifikationen. Man kann zum Beispiel Binärdaten hinter einem BASIC-Programm speichern. Beim C64 war das kein Problem auf diese Weise ein BASIC-Programm zusammen mit Maschinenprogrammteilen und/oder (Grafik)Daten in einer Datei zu speichern. Solange Zeilen kein Sprungziel sind, kann man die Zeilennummer auch beliebig manipulieren. Zum Beispiel all diese Zeilen auf die Zeilennummer 0 setzen. War beliebt um Programme vor Veränderungen zu schützen.
Du vermischt hier IMHO auch zwei Ebenen. Das eine ist das Speicherformat auf dem Band und das andere ist das Speicherformat für BASIC-Programme. Die Bandroutinen sollten wie schon gesagt so entworfen sein, dass für alle Dateitypen das Laden/Lesen grundsätzlich gleich funktioniert. Damit man eben nicht für jedem Typ eigene Laderoutinen braucht. Der Laderoutine ist der Blocktyp oder der Dateityp egal, die funktioniert immer gleich.
Du vermischt hier IMHO auch zwei Ebenen. Das eine ist das Speicherformat auf dem Band und das andere ist das Speicherformat für BASIC-Programme. Die Bandroutinen sollten wie schon gesagt so entworfen sein, dass für alle Dateitypen das Laden/Lesen grundsätzlich gleich funktioniert. Damit man eben nicht für jedem Typ eigene Laderoutinen braucht. Der Laderoutine ist der Blocktyp oder der Dateityp egal, die funktioniert immer gleich.
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
Aber wie soll das gehen? Eigentlich darf kein einziges 0x00 vorkommen, denn das markiert ja das Ende der BASIC Zeile.BlackJack hat geschrieben:*innerhalb* eines BASIC-Programms drei Nullbytes vorkommen
Ansonsten könnte man die Zeilen nicht richtig trennen.
Hab auch angefangen, neben dem tokenized BASIC auch ASCII BASIC zu behandeln, hier: https://github.com/jedie/PyDragon32/blo ... DC.py#L717
Der wird aber bisher nur angezeigt, mehr nicht.
Interessant dabei: Beim tokenized BASIC wird das Ende des Codes mit zwei 0x00 extra Markiert. Beim ASCII BASIC sind die Zeilen mit einem \r getrennt. Sieht so aus:
Hätte jetzt zwei \r nochmal am Ende erwartet...\r1 PRINT "LINE NUMBER TEST"\r
\r10 PRINT 10\r
\r100 PRINT 100\r
\r1000 PRINT 1000\r
\r10000 PRINT 10000\r
\r32768 PRINT 32768\r
\r63999 PRINT "END";63999\r
Auch hier darf dann im BASIC-Code kein \r vorkommen. Keine Ahnung ob man das überhaupt einfügen kann. Aber evtl. wird es dann escaped?
Ich weiß noch nicht wie man sowas machen kann und ob der Dragon das kann.BlackJack hat geschrieben:Man kann zum Beispiel Binärdaten hinter einem BASIC-Programm speichern. Beim C64 war das kein Problem auf diese Weise ein BASIC-Programm zusammen mit Maschinenprogrammteilen und/oder (Grafik)Daten in einer Datei zu speichern. Solange Zeilen kein Sprungziel sind, kann man die Zeilennummer auch beliebig manipulieren. Zum Beispiel all diese Zeilen auf die Zeilennummer 0 setzen. War beliebt um Programme vor Veränderungen zu schützen.
Hab zwar schon damit gerechnet, das man BASIC+Daten+BIN speichern kann, aber ich bin davon ausgegenagen, das das dann immer in einem neuen Block passiert. Es also immer mehrere Dateien sind.
Hast du info's dazu? Evtl. vom C64 und man kann das "Übertragen" ?
Nein, ich weiß um die beiden Ebenen. Nur weiß ich nicht ob wir vom selben redenBlackJack hat geschrieben:Du vermischt hier IMHO auch zwei Ebenen. Das eine ist das Speicherformat auf dem Band und das andere ist das Speicherformat für BASIC-Programme. Die Bandroutinen sollten wie schon gesagt so entworfen sein, dass für alle Dateitypen das Laden/Lesen grundsätzlich gleich funktioniert. Damit man eben nicht für jedem Typ eigene Laderoutinen braucht. Der Laderoutine ist der Blocktyp oder der Dateityp egal, die funktioniert immer gleich.

Eigentlich sind das mehrere Ebenen:
* WAVE <-> bits (Also wie die Binäredaten im Ton moduliert werden, das macht IMHO der C64 anders)
* Blockebene: Die ich mir z.Z. mit get_block_info() durchgehe: https://github.com/jedie/PyDragon32/blo ... DC.py#L543
* Block Daten: Fileinfo-Block (Dateiname, Typ, Länge usw.) / Datei-Inhalt
EDIT: Gerade das nochmal ins Auge gesprugen:
Ist in den Meta-Daten des Fileinfo-Blocks...5.4 A gap flag to indicate whether the
data stream is continuous (00) as
in binary or BASIC files, or in blocks
where the tape keeps stopping (FF) as
in data files.
Keine Ahnung was das wirklich bedeutet. Habe festgestellt, das es pausen zwischen den Blöcken gibt. Ob das damit gemeint ist?
@jens: Natürlich dürfen noch andere Nullbytes vorkommen. Sonst könnte man keine Zeilennummer verwenden die als 16-Bit-Wert ein Nullbyte enthält, insbesondere nicht die Zeilennummer 0, die sogar zu *zwei* Nullbytes wird. Und es dürfte keine BASIC-Zeile an einer Speicheradresse beginnen deren High- oder Lowbyte Null ist. Womit dann auch klar ist wie man völlig legal drei Nullbytes hinbekommt: Eine Zeile mit der Zeilennummer 0 die so im Speicher platziert ist, dass das Lowbyte der Adresse der nächsten Zeile 0 ist.
Aber wie schon gesagt, die Leute stellen manchmal die abenteuerlichsten Sachen an. Die Beschreibung der Spezifikation sagt ja zum Beispiel nichts darüber aus, dass die Zeilennummern in jeder Zeile verschieden sein müssen. Man kann auch einfach allen Zeilen die kein Sprungziel sind, die Zeilennnummer 0 verpassen. Damit steigt die Wahrscheinlichkeit drei Nullbytes in den Daten zu bekommen. Das könnte zu Problemen mit Sprunganweisung die im Programm *zurück* springen geben, darum wird das so gelöst, dass die Zeilennummern immer bei einer Zeile die Sprungziel sind, um 1 hochgezählt wird. Sinn davon ist übrigens nicht nur das Bearbeiten zu erschweren, sondern auch Programme kürzer zu machen, denn die Sprungziele werden im Quelltext als Ziffernfolge gespeichert. Kurze Zeilennummern bedeuten also weniger Bytes.
Das '\r' ist ja die Return-Taste. Wie würdest Du das denn eingeben wollen? Wenn Du die betätigst wird ja die Zeile übernommen und in den Speicher geschrieben.
Beim C64 hat man einfach die verschiedenen Dateien in den Speicher geladen. Die Zeiger für das Ende des BASIC-Programms hinter die letzten Daten gesetzt und dann den BASIC-Speicherbefehl aufgerufen.
Wave->Bits macht der C64 sehr ähnlich und diverse TurboTape-Routinen auf dem C64 noch ähnlicher. Die beiden Bitwerte haben jeweils eine Pulslänge zugeordnet, wie beim Dragon.
Kleine Pausen werden auch bei „continuous”-Dateien sein. Technisch bedingt. Bei Datendateien werden die grösser sein, weil das ja wahrscheinlich so läuft, dass es einen Blockpuffer im Speicher gibt, der immer wenn er voll ist geschrieben wird, oder beim lesen immer wenn er leer ist, der nächste Block vom Band geladen wird. Dafür muss vor jedem Block der Bandmotor gestartet und nach dem Block der Motor wieder gestoppt werden. So entstehen an sich schon Lücken, die dann wahrscheinlich noch etwas verlängert werden um Laufwerksunterschiede auszugleichen.
Aber wie schon gesagt, die Leute stellen manchmal die abenteuerlichsten Sachen an. Die Beschreibung der Spezifikation sagt ja zum Beispiel nichts darüber aus, dass die Zeilennummern in jeder Zeile verschieden sein müssen. Man kann auch einfach allen Zeilen die kein Sprungziel sind, die Zeilennnummer 0 verpassen. Damit steigt die Wahrscheinlichkeit drei Nullbytes in den Daten zu bekommen. Das könnte zu Problemen mit Sprunganweisung die im Programm *zurück* springen geben, darum wird das so gelöst, dass die Zeilennummern immer bei einer Zeile die Sprungziel sind, um 1 hochgezählt wird. Sinn davon ist übrigens nicht nur das Bearbeiten zu erschweren, sondern auch Programme kürzer zu machen, denn die Sprungziele werden im Quelltext als Ziffernfolge gespeichert. Kurze Zeilennummern bedeuten also weniger Bytes.
Das '\r' ist ja die Return-Taste. Wie würdest Du das denn eingeben wollen? Wenn Du die betätigst wird ja die Zeile übernommen und in den Speicher geschrieben.
Beim C64 hat man einfach die verschiedenen Dateien in den Speicher geladen. Die Zeiger für das Ende des BASIC-Programms hinter die letzten Daten gesetzt und dann den BASIC-Speicherbefehl aufgerufen.
Wave->Bits macht der C64 sehr ähnlich und diverse TurboTape-Routinen auf dem C64 noch ähnlicher. Die beiden Bitwerte haben jeweils eine Pulslänge zugeordnet, wie beim Dragon.
Kleine Pausen werden auch bei „continuous”-Dateien sein. Technisch bedingt. Bei Datendateien werden die grösser sein, weil das ja wahrscheinlich so läuft, dass es einen Blockpuffer im Speicher gibt, der immer wenn er voll ist geschrieben wird, oder beim lesen immer wenn er leer ist, der nächste Block vom Band geladen wird. Dafür muss vor jedem Block der Bandmotor gestartet und nach dem Block der Motor wieder gestoppt werden. So entstehen an sich schon Lücken, die dann wahrscheinlich noch etwas verlängert werden um Laufwerksunterschiede auszugleichen.
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
Also du meinst 0x00 kann vorkommen z.B. bei GOTO ?
Wenn das wirklich so ist, wie soll ich dann die Zeilen parsen können?
Ich hab nur flüchtig auf das Bild dort geblickt und dachte, das würde was mit der Amplitude gemacht werden. Aber offensichtlich geht es da auch nur um die Länge...
Wäre ja interessant, das ganze dann modular auch für den C64 zu bauen.
Ich glaube ich habe auch einen Denkfehler beim umwandeln der WAVE in den Bit-Strom: Denn bei https://github.com/jedie/PyDragon32/blo ... DC.py#L266 suche ich immer nach einer kompletten Sinus-Kurve. Dann schaue ich wie lange die war und unterscheide zwischen 0 und 1
Ich denke das Problem liegt darin, das ich bei falscher even_odd Vorgabe, eine Hälfte der Sinuskurve von z.B. 1 zur anderen Hälfte von 0 zu zähle. In dem Fall kommt natürlich Müll raus.
Somit sollte ich lieber die Zeiten der Halben Sinus-Kurve messen. Also doch einfach die Nulldurchgänge... Doch echte Nulldurchgänge vom Rauschen Unterscheiden, wäre dabei das Problem.
Allerdings kann ich dabei vielleicht bei dem Aktuellen Algorithmus bleiben (Kommt ja nicht auf die Absolut genaue Zeit des Nulldurchgangs an): https://github.com/jedie/PyDragon32/blo ... DC.py#L203
Im Groben: Ein Nulldurchgang ist immer dann geschehen, wenn mehrere MIN_TOGGLE_COUNT Sample-Werte im Positiven oder Negativen Bereich sind.
Vielleicht das ganze in drei hintereinander geschalteten Generatoren:
1. Generator liefert die WAVE-Samples zurück iter_wave_values(): https://github.com/jedie/PyDragon32/blo ... DC.py#L134
2. Generator wartet auf die mehrere MIN_TOGGLE_COUNT Sample-Werte im Positiven oder Negativen Bereich und spuckt dann nur die rohen Zeiten aus.
3. Generator nimmt die Zeiten entgehen und bildet damit den Bit-Stream
Wenn das wirklich so ist, wie soll ich dann die Zeilen parsen können?
Stimmt: http://en.wikipedia.org/wiki/Datasette#Physical_codingBlackJack hat geschrieben:Wave->Bits macht der C64 sehr ähnlich und diverse TurboTape-Routinen auf dem C64 noch ähnlicher. Die beiden Bitwerte haben jeweils eine Pulslänge zugeordnet, wie beim Dragon.
Ich hab nur flüchtig auf das Bild dort geblickt und dachte, das würde was mit der Amplitude gemacht werden. Aber offensichtlich geht es da auch nur um die Länge...
Wäre ja interessant, das ganze dann modular auch für den C64 zu bauen.
Ich glaube ich habe auch einen Denkfehler beim umwandeln der WAVE in den Bit-Strom: Denn bei https://github.com/jedie/PyDragon32/blo ... DC.py#L266 suche ich immer nach einer kompletten Sinus-Kurve. Dann schaue ich wie lange die war und unterscheide zwischen 0 und 1
Ich denke das Problem liegt darin, das ich bei falscher even_odd Vorgabe, eine Hälfte der Sinuskurve von z.B. 1 zur anderen Hälfte von 0 zu zähle. In dem Fall kommt natürlich Müll raus.
Somit sollte ich lieber die Zeiten der Halben Sinus-Kurve messen. Also doch einfach die Nulldurchgänge... Doch echte Nulldurchgänge vom Rauschen Unterscheiden, wäre dabei das Problem.
Allerdings kann ich dabei vielleicht bei dem Aktuellen Algorithmus bleiben (Kommt ja nicht auf die Absolut genaue Zeit des Nulldurchgangs an): https://github.com/jedie/PyDragon32/blo ... DC.py#L203
Im Groben: Ein Nulldurchgang ist immer dann geschehen, wenn mehrere MIN_TOGGLE_COUNT Sample-Werte im Positiven oder Negativen Bereich sind.
Vielleicht das ganze in drei hintereinander geschalteten Generatoren:
1. Generator liefert die WAVE-Samples zurück iter_wave_values(): https://github.com/jedie/PyDragon32/blo ... DC.py#L134
2. Generator wartet auf die mehrere MIN_TOGGLE_COUNT Sample-Werte im Positiven oder Negativen Bereich und spuckt dann nur die rohen Zeiten aus.
3. Generator nimmt die Zeiten entgehen und bildet damit den Bit-Stream
@jens: Nein, nicht bei GOTO sondern bei den Zeilennummern und den Pointern auf die nächste Zeile. Da kann im Grunde jeder beliebige Bytewert, also auch die 0 vorkommen. Danach bis zum Ende der Zeile sollte kein Nullbyte mehr vorkommen.
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
Achso... Deswegen hab ich dich nicht verstanden... Ja klar, da kommen Null-Bytes vor. Aber das ist nicht das Problem.BlackJack hat geschrieben:@jens: Nein, nicht bei GOTO sondern bei den Zeilennummern und den Pointern auf die nächste Zeile. Da kann im Grunde jeder beliebige Bytewert, also auch die 0 vorkommen. Danach bis zum Ende der Zeile sollte kein Nullbyte mehr vorkommen.
Es kommen immer 2-Byte "Line-Pointer" dann 2-Byte "Zeilen-Nummer" dann X-Bytes Code bis 0x00
@jens: Ja, aber wenn Du das *so* verarbeitest, an welcher Stelle suchst Du denn dann nach drei Nullbytes? Darum ging es doch. Das Ende erkennt man in dem Fall doch an einem Nullpointer, also *zwei* Nullbytes, und auch nicht ohne Kontext, sondern in dem Wissen, dass man sich gerade am Anfang von den Daten einer neuen Zeile befindet. Und das hat ja schon alles nichts mehr mit dem Lesen vom Band zu tun.
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
Wüßte nicht wie man es anders machen kann.
An dieser Stelle ist das mit den zwei 0x00 Bytes: https://github.com/jedie/PyDragon32/blo ... DC.py#L699
Siehe auch das Beispiel von hier: http://www.python-forum.de/viewtopic.ph ... 35#p244435
EDIT: An dieser Stelle geht es um das Parsen der Roh "tokenized BASIC source code"
An dieser Stelle ist das mit den zwei 0x00 Bytes: https://github.com/jedie/PyDragon32/blo ... DC.py#L699
Siehe auch das Beispiel von hier: http://www.python-forum.de/viewtopic.ph ... 35#p244435
EDIT: An dieser Stelle geht es um das Parsen der Roh "tokenized BASIC source code"
@jens: Ähm, der Start dieser (Teil)Diskussion war, dass Du gesagt hast man könnte das Ende eines BASIC-Programms an den drei Nullbytes erkennen und darum wäre die Längenangabe überflüssig. Das Stand mal in diesem Beitrag: http://www.python-forum.de/viewtopic.ph ... 70#p244470
Hier mal eine Hilfsklasse zum Umwandeln von Bits in Integern, falls du es gebrauchen kannst:jens hat geschrieben:So... um es nochmal klar zu machen, was da überhaupt passiert:
1. WAV Sinus-Kurvenlänge -> Bitstream aus Nullen und Einsen
2. suche nach Muster "10101010" (0x55) Zeichenweise
3. in 8-Bit-Schritten weiter Springen, bis das Muster "10101010" nicht mehr vorkommt.
4. Suchen des Sync-Bits "00111100" (0x3C) Zeichenweise
Code: Alles auswählen
import collections
class BitContainer(object):
def __init__(self, bits=[], max_size=8):
self._bits = collections.deque(maxlen=max_size)
self.feed_iterable(bits)
def __repr__(self):
return '{}({})'.format(type(self).__name__, self.to_string())
def __str__(self):
return self.to_string()
def __len__(self):
return len(self._bits)
def __int__(self):
return self.to_integer()
def __iter__(self):
return iter(self._bits)
@property
def bits(self):
return list(self._bits)
def feed(self, bit):
bit = int(bit)
if not bit in (0, 1):
raise ValueError('bit must be 0 or 1')
self._bits.append(bit)
def feed_iterable(self, bits):
for bit in bits:
self.feed(bit)
def to_integer(self):
return sum(
bit << i for i, bit in enumerate(reversed(self._bits))
)
def to_string(self):
return ''.join(map(str, self._bits))
Code: Alles auswählen
bc = BitContainer()
for bit in bitstream:
bc.feed(bit)
if int(bc) == 0x55:
break

- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
ja, anders herum: Die beiden 0x00 Bytes am Ende sind eigentlich Überflüssig. Denn wenn Block zu Ende, dann BASIC code zuende...BlackJack hat geschrieben:@jens: Ähm, der Start dieser (Teil)Diskussion war, dass Du gesagt hast man könnte das Ende eines BASIC-Programms an den drei Nullbytes erkennen und darum wäre die Längenangabe überflüssig. Das Stand mal in diesem Beitrag: http://www.python-forum.de/viewtopic.ph ... 70#p244470
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
So, hab den ASCII BASIC Parser fertig mit: https://github.com/jedie/PyDragon32/com ... 8130d21cfb
Dann noch die Block-Längenangabe geprüft: https://github.com/jedie/PyDragon32/com ... a821917447
Wobei das ein wenig Witzlos ist, denn ich schneide ja anhand der Block Länge auch wirklich den Code raus. Von daher kann die Länge ja beim parsen nicht anders sein...
@snafu: Deinen Code muß ich mir später ansehen.
Generell weiß ich nicht, wie früh man die Bits in Bytes umformen kann/sollte.
Dann noch die Block-Längenangabe geprüft: https://github.com/jedie/PyDragon32/com ... a821917447
Wobei das ein wenig Witzlos ist, denn ich schneide ja anhand der Block Länge auch wirklich den Code raus. Von daher kann die Länge ja beim parsen nicht anders sein...
@snafu: Deinen Code muß ich mir später ansehen.
Generell weiß ich nicht, wie früh man die Bits in Bytes umformen kann/sollte.
In welchem Zusammenhang meinst du das? Wegen möglichen Fehlinterpretationen, wenn man zu früh umwandelt, oder warum? Falls die Spec nichts anderes vorgibt, dann würde ich doch mal davon ausgehen, dass 1 Byte = 8 Bit.jens hat geschrieben:Generell weiß ich nicht, wie früh man die Bits in Bytes umformen kann/sollte.
Falls man sich eh immer solche Päckchen holt, dann braucht man natürlich keinen BitContainer mehr, sondern wirft die Päckchen einfach in eine Funktion ähnlich zu der `.to_integer()`-Methode in meinem Code.
@snafu: Warum der Umweg über deque?
Code: Alles auswählen
class BitContainer(object):
def __init__(self, bits=[], max_size=8):
self.bits = 0
self._mask = (1 << max_size) - 1
self.feed_iterable(bits)
def feed(self, bit):
bit = int(bit)
if not bit in (0, 1):
raise ValueError('bit must be 0 or 1')
self.bits = ((self.bits << 1) | bit) & self._mask
def feed_iterable(self, bits):
for bit in bits:
self.feed(bit)
Das Leben ist wie ein Tennisball.
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
Ich z.B. nicht direkt nach dem Sync-Bit los legen: Zumindest nicht direkt von 8-Bits in Zeichen wandeln. Also quasi: chr(int([0,0,1,1,0,0,1,0], 2))snafu hat geschrieben:In welchem Zusammenhang meinst du das? Wegen möglichen Fehlinterpretationen, wenn man zu früh umwandelt, oder warum? Falls die Spec nichts anderes vorgibt, dann würde ich doch mal davon ausgehen, dass 1 Byte = 8 Bit.jens hat geschrieben:Generell weiß ich nicht, wie früh man die Bits in Bytes umformen kann/sollte.
Denn es gibt, auch wenn es eine 8-Bit-Rechner ist, dennoch einige 16-Bit Werte:
Siehe: http://de.wikipedia.org/wiki/Motorola_6809Trotz des 8 Bit breiten Datenbusses konnten intern zwei 8-Bit-Register zu einem 16-Bit-Register zusammengefasst werden.
Was ich machen könnte: Nach dem Sync-Bit nur das machen: int([0,0,1,1,0,0,1,0], 2) (btw. wie nennt man am Besten das was da raus kommt? Ich habs bisher meist byte_no genannt. Trifft es aber IMHO nicht ganz.)
@EyDu: Danke für den Code. Werde ich mir auch noch anschauen müßen...
Ich will aber als erstes nochmal an den WAVE->Bits Algorithmus ran. Ich will das doofe even_odd loswerden.
@jens: Die 8 Bits zu einem `int` umgewandelt würde ich ja einfach `byte` oder `byte_value` nennen. Denn das ist es ja. 
Das es auch 16-Bit-Werte geben kann hat nichts mit dem Prozessor zu tun. Das die falsche Ebene. Der C64 hat keine 16-Bit-Register (aus dem PC, aber den benutzt man ja nicht direkt) und das BASIC-Format sieht grundsätzlich genau so aus. Die 16-Bit-Werte sind in Anwendungsdaten. Wobei die Anwendung hier der BASIC-Interpreter ist. Man kann auch auf Prozessoren mit 8- oder 16-Bit-Registern Werte in den Daten haben die 24 oder 32 Bits haben. Oder noch mehr.
Ich würde die Pulse ja direkt in einen Bytewert umwandeln sowie ein Bit erkannt wurde. Da man die von links in den Bytewert hineinschiebt, braucht man sich nicht einmal um die überzähligen Bits zu kümmern, die fallen automatisch weg. Erscheint mir am einfachsten und direktesten.
Wenn man das in eine eigene Klasse verpacken möchte (ungetestet):
Dann kann man zum Syncbyte finden zum Beispiel so etwas schreiben:

Das es auch 16-Bit-Werte geben kann hat nichts mit dem Prozessor zu tun. Das die falsche Ebene. Der C64 hat keine 16-Bit-Register (aus dem PC, aber den benutzt man ja nicht direkt) und das BASIC-Format sieht grundsätzlich genau so aus. Die 16-Bit-Werte sind in Anwendungsdaten. Wobei die Anwendung hier der BASIC-Interpreter ist. Man kann auch auf Prozessoren mit 8- oder 16-Bit-Registern Werte in den Daten haben die 24 oder 32 Bits haben. Oder noch mehr.
Ich würde die Pulse ja direkt in einen Bytewert umwandeln sowie ein Bit erkannt wurde. Da man die von links in den Bytewert hineinschiebt, braucht man sich nicht einmal um die überzähligen Bits zu kümmern, die fallen automatisch weg. Erscheint mir am einfachsten und direktesten.
Wenn man das in eine eigene Klasse verpacken möchte (ungetestet):
Code: Alles auswählen
class ShiftRegister(object):
def __init__(self, length=8, start_value=0):
self.value = start_value
self.mask = 1 << length
def __int__(self):
return self.value
def __cmp__(self, other):
return cmp(int(self), int(other))
def add_bit(self, bit_value):
self.value = (self.value >> 1) | (self.mask if bit_value else 0)
Code: Alles auswählen
while self.shift_register != 0x3c:
self.shift_register.add_bit(next(bits))
Weil ich offen gestanden bloß Grundkenntnisse zur sinnvollen Anwendung von Bit-Operationen habe und mir schlichtweg nichts besseres eingefallen ist.EyDu hat geschrieben:@snafu: Warum der Umweg über deque?

- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
Ich brauche nochmal hilfe.
Ich hab nochmal neu ein wave2bitstream implementiert. Nun nach dem Schmitt-trigger prinzip. Ziel ist es, von dem even_odd weg zu kommen und automatisch zu synchronisieren.
Aktueller Stand hier: https://github.com/jedie/PyDragon32/blo ... tstream.py
Diesmal sind es mehrmals hintereinander geschaltete Interatoren:
1. iter_wave_values() - yield Frame Nummer und Amplituden-Wert (alter code) -> https://github.com/jedie/PyDragon32/blo ... am.py#L194
2. iter_trigger() - yield Frame Nummer nach dem Schmitt Trigger Prinzip:
-> https://github.com/jedie/PyDragon32/blo ... am.py#L179
3. iter_duration() - Berechnet die Zweiten zwischen den getriggerten Frames:
-> https://github.com/jedie/PyDragon32/blo ... am.py#L169
4. iter_bitstream() - Aus den Zeiten werden einsen und nullen (alter code) -> https://github.com/jedie/PyDragon32/blo ... am.py#L106
Nun fehlt noch das synchronisieren zwischen den Halb-Sinus-Wellen... Dazu hatte ich zwei Ideen:
1. Man nimmt einen Teil des Bitstream und schaut nach dem Leading-Byte Muste wie 0101010...
2. Man schaut sich die Zeiten der Halbwellen an und überlegt ob die Zusammen passen.
Hab mich für die zweite Lösung entschieden, weil sie unabhängig vom verwendeten Leading-Byte Muster ist (Das kann man dann später suchen)...
Dazu habe ich eine, nicht so besonders optimierte Lösung:
-> https://github.com/jedie/PyDragon32/blo ... ls.py#L246
Also die Idee, ist, das im Bitstrom die Halb-Sinus-Wellen-Dauer immer die selbe Zeit haben (mit Toleranzen natürlich).
Ein Beispiel:
Beim Dragon ist eine 1 ~ 2400Hz und 2 ~ 1200Hz
Die Dauer wäre dann, weil es einfach ist: 1==10 und 0==20
Die Halbwelle ist halt die hälfte also nehmen wir mal die Beispielwerte 1/2 1==5 und 1/2 0==10 an...
Der Bitstrom ...,1,0,1,0,... wäre dann also diese Zeiten der Halbwellen:
...,5,5,10,10,5,5,10,10,...
Wenn es nicht synchron ist, dann sind die Werte um eine Halbwelle verschoben, also beispielsweise:
...,5,10,10,5,5,10,10,5,...
Mit diff_info() kann man das dann feststellen. Sie DocTest darin...
Die Idee war nun, das man vor dem eigentlichen Auslesen einen Test machen sync() und dann evtl. eine Halbwelle weiterspringt.
Genau das klappt aber z.Z. nicht, siehe: https://github.com/jedie/PyDragon32/blo ... eam.py#L73
Für das "Weiterspringen" dache ich mir, lese ich einfach einen Wert aus und gut -> https://github.com/jedie/PyDragon32/blo ... eam.py#L89
Tut es aber nicht.
Warum?
Ich hab nochmal neu ein wave2bitstream implementiert. Nun nach dem Schmitt-trigger prinzip. Ziel ist es, von dem even_odd weg zu kommen und automatisch zu synchronisieren.
Aktueller Stand hier: https://github.com/jedie/PyDragon32/blo ... tstream.py
Diesmal sind es mehrmals hintereinander geschaltete Interatoren:
1. iter_wave_values() - yield Frame Nummer und Amplituden-Wert (alter code) -> https://github.com/jedie/PyDragon32/blo ... am.py#L194
2. iter_trigger() - yield Frame Nummer nach dem Schmitt Trigger Prinzip:
Code: Alles auswählen
def iter_trigger(self, iter_wave_values, half_sinus):
"""
yield only the triggered frame numbers
simmilar to a Schmitt trigger
"""
last_state = False
for frame_no, value in iter_wave_values:
if last_state == False and value > self.trigger_value:
yield frame_no
last_state = True
elif last_state == True and value < -self.trigger_value:
if half_sinus:
yield frame_no
last_state = False
3. iter_duration() - Berechnet die Zweiten zwischen den getriggerten Frames:
Code: Alles auswählen
def iter_duration(self, iter_trigger):
"""
yield the duration of two frames in a row.
"""
old_frame_no = next(iter_trigger)
for frame_no in iter_trigger:
duration = frame_no - old_frame_no
yield (frame_no, duration)
old_frame_no = frame_no
4. iter_bitstream() - Aus den Zeiten werden einsen und nullen (alter code) -> https://github.com/jedie/PyDragon32/blo ... am.py#L106
Nun fehlt noch das synchronisieren zwischen den Halb-Sinus-Wellen... Dazu hatte ich zwei Ideen:
1. Man nimmt einen Teil des Bitstream und schaut nach dem Leading-Byte Muste wie 0101010...
2. Man schaut sich die Zeiten der Halbwellen an und überlegt ob die Zusammen passen.
Hab mich für die zweite Lösung entschieden, weil sie unabhängig vom verwendeten Leading-Byte Muster ist (Das kann man dann später suchen)...
Dazu habe ich eine, nicht so besonders optimierte Lösung:
Code: Alles auswählen
def diff_info(data):
"""
>>> diff_info([5,5,10,10,5,5,10,10])
(0, 15)
>>> diff_info([5,10,10,5,5,10,10,5])
(15, 0)
"""
def get_diff(l):
diff = 0
for no1, no2 in iter_steps(l, steps=2):
diff += abs(no1 - no2)
return diff
data1 = data[2:]
diff1 = get_diff(data1)
data2 = data[1:-1]
diff2 = get_diff(data2)
return diff1, diff2
Also die Idee, ist, das im Bitstrom die Halb-Sinus-Wellen-Dauer immer die selbe Zeit haben (mit Toleranzen natürlich).
Ein Beispiel:
Beim Dragon ist eine 1 ~ 2400Hz und 2 ~ 1200Hz
Die Dauer wäre dann, weil es einfach ist: 1==10 und 0==20
Die Halbwelle ist halt die hälfte also nehmen wir mal die Beispielwerte 1/2 1==5 und 1/2 0==10 an...
Der Bitstrom ...,1,0,1,0,... wäre dann also diese Zeiten der Halbwellen:
...,5,5,10,10,5,5,10,10,...
Wenn es nicht synchron ist, dann sind die Werte um eine Halbwelle verschoben, also beispielsweise:
...,5,10,10,5,5,10,10,5,...
Mit diff_info() kann man das dann feststellen. Sie DocTest darin...
Die Idee war nun, das man vor dem eigentlichen Auslesen einen Test machen sync() und dann evtl. eine Halbwelle weiterspringt.
Genau das klappt aber z.Z. nicht, siehe: https://github.com/jedie/PyDragon32/blo ... eam.py#L73
Für das "Weiterspringen" dache ich mir, lese ich einfach einen Wert aus und gut -> https://github.com/jedie/PyDragon32/blo ... eam.py#L89
Tut es aber nicht.
Warum?
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
Hm. Ich glaube ich habe Fehler weil ich u.a. Iterator und Generator durcheinander würfle
):
So, mit https://github.com/jedie/PyDragon32/com ... 96e7a22d9c kann man .sync() innerhalb des Interatiors aufrufen.
Aber irgendwie scheint es egal zu sein, ob ich eine halbwelle weiter springe oder nicht?!?!

So, mit https://github.com/jedie/PyDragon32/com ... 96e7a22d9c kann man .sync() innerhalb des Interatiors aufrufen.
Aber irgendwie scheint es egal zu sein, ob ich eine halbwelle weiter springe oder nicht?!?!