Translate project pages

This commit is contained in:
Kai Vogelgesang
2025-09-19 23:21:37 +02:00
parent 899add52f1
commit 50239a4d90
5 changed files with 160 additions and 159 deletions

View File

@@ -25,18 +25,18 @@
"id": "6ab69020",
"metadata": {},
"source": [
"In diesem Projekt arbeiten wir mit Audio-Daten.\n",
"Um das Einlesen möglichst unkompliziert zu halten, stellen wir die Eingabe in einem einfachen Format bereit:\n",
"In this project, we work with audio data.\n",
"To facilitate reading it into your program, we provide the inputs in a simple format:\n",
"\n",
"```{admonition} Format\n",
"Die erste Zeile enthält $n$, die Anzahl der Samples ($0 \\leq n < 2^{32}$).\n",
"The first line contains $n$, the number of samples ($0 \\leq n < 2^{32}$).\n",
"\n",
"Danach folgen $n$ Zeilen mit jeweils einem Sample $x$ als ganze Zahl ($-2^{15} \\leq x < 2^{15}$)\n",
"Afterwards, $n$ lines follow, each with one sample $x$ as an integer ($-2^{15} \\leq x < 2^{15}$).\n",
"```\n",
"\n",
"Die Abtastrate $f_S$ ist dabei 16640 Hz.\n",
"The sampling rate $f_s$ is fixed at 16640 Hz.\n",
"\n",
"## Beispiel\n",
"## Example\n",
"```\n",
"10\n",
"623\n",
@@ -52,7 +52,7 @@
"```\n",
"\n",
"```{note}\n",
"Die Werte der Samples sind hier zwar ganze Zahlen, aber für den Rest des Projekts ergibt es Sinn sie hier schon in Floating-Point Werte umzuwandeln.\n",
"Although the samples are given as integer numbers, for the rest of the project it makes sense to convert them into a floating point format at this point already.\n",
"```"
]
},
@@ -61,32 +61,32 @@
"id": "cfcac6c9",
"metadata": {},
"source": [
"# Visualisierung\n",
"# Visualization\n",
"\n",
"In diesem Projekt kann es sehr hilfreich sein, sich nach jedem Arbeitsschritt das Ergebnis zu visualisieren.\n",
"Das können wir zum Beispiel so tun:\n",
"- Das Signal enthält 2 Zeilen mit je 2080 Pixeln pro Sekunde. Mit $f_S = 16640\\text{Hz}$ haben wir also 4 Samples pro Pixel.\n",
"- Wir nehmen jedes 4. Sample, falls es komplex ist berechnen wir den Betrag $(a + ib \\mapsto \\sqrt{a^2+b^2} =\\mathrel{\\vcenter{:}} v)$.\n",
"- Wir finden $v_{min}$ und $v_{max}$, den kleinsten bzw. größten so erhaltenen Wert.\n",
" Dann skalieren wir jedes $v$ nach $255 \\cdot (v - v_{min}) / (v_{max} - v_{min})$.\n",
" Damit bekommen wir einen Wert zwischen 0 und 255.\n",
"- Diese Werte können wir jetzt als Pixel in einer Bilddatei abspeichern.\n",
" Dazu können wir zum Beispiel das [](pgm-format) benutzen.\n",
"It can be helpful during this project to visualize the result after every step.\n",
"Wen can do this for example like this:\n",
"- The signal contains 2 lines with 2080 pixels each per second. With $f_S = 16640\\text{Hz}$, we have 4 samples per pixel.\n",
"- We take every 4th sample. For complex numbers, we calculate the magnitude $(a + ib \\mapsto \\sqrt{a^2+b^2} =\\mathrel{\\vcenter{:}} v)$.\n",
"- We find $v_{min}$ and $v_{max}$, the smallest and largest values we obtained.\n",
" Then we scale each $v$ to $255 \\cdot (v - v_{min}) / (v_{max} - v_{min})$.\n",
" The result is a value between 0 and 255.\n",
"- We can save these values as pixels in an image file.\n",
" To do this, we can use the [](pgm-format) for example.\n",
" \n",
"```{figure} img/reference/raw_full_scaled.webp\n",
"---\n",
"name: fig:raw_full.en\n",
"---\n",
"Visualisierung direkt nach dem Einlesen.\n",
"Man kann das Bild fast schon erkennen, es ist aber noch sehr dunkel.\n",
"Visualization, directly after reading the input.\n",
"The image can be discerned already, but it is quite dark.\n",
"```\n",
"\n",
"```{figure} img/reference/raw_detail.webp\n",
"---\n",
"name: fig:raw_detail.en\n",
"---\n",
"Detailansicht.\n",
"Hier sieht man deutliche vertikale Streifen, die von der Modulation auf den 2.4kHz Carrier kommen.\n",
"Detail view.\n",
"We can clearly see vertical lines, stemming from the modulation onto the 2.4 kHz carrier.\n",
"```"
]
}

View File

@@ -5,7 +5,7 @@
"id": "e67c2a43",
"metadata": {},
"source": [
"# Frequenz-Shift"
"# Frequency Shift"
]
},
{
@@ -17,7 +17,7 @@
"---\n",
"name: fig:raw_waterfall.en\n",
"---\n",
"Wasserfall-Diagramm. Wir können die beiden Kopien des Spektrums bei $\\pm2.4\\text{kHz}$ erkennen.\n",
"Waterfall diagram. We can see both copies of the spectrum at $\\pm2.4\\text{kHz}$.\n",
"```"
]
},
@@ -26,64 +26,64 @@
"id": "cbe50415",
"metadata": {},
"source": [
"Als ersten Schritt wollen wir die Kopie des Spektrums bei 2.4kHz \"auf die 0\" verschieben.\n",
"As a first step, we want to shift the copy of the spectrum at 2.4 kHz to zero.\n",
"\n",
"Im Frequenz-Bereich können wir diese Verschiebung als eine Faltung mit einem Peak bei -2.4kHz darstellen.\n",
"Wie können wir so einen Peak erzeugen?\n",
"In the frequency domain, we can represent this shift as a convolution with a singular peak at -2.4kHz.\n",
"How can we create such a peak?\n",
"\n",
"Wir erinnern uns:\n",
"$\\sin(2\\pi f t)$ und $\\cos(2\\pi f t)$ hatten Peaks bei $\\pm f$.\n",
"We remember:\n",
"$\\sin(2\\pi f t)$ and $\\cos(2\\pi f t)$ have peaks at $\\pm f$.\n",
"\n",
"Genauer gesagt:\n",
"To be precise:\n",
"```{math}\n",
"\\begin{align*}\n",
"\\sin(2\\pi f_0 t) \\,&\\circ\\!\\!-\\!\\!\\bullet\\, \\left\\{\\begin{array}{rcl}\n",
" \\frac{1}{2}i && f = -f_0 \\\\\n",
" -\\frac{1}{2}i &:& f = f_0 \\\\\n",
" 0 &:& \\text{sonst} \\\\\n",
" 0 &:& \\text{otherwise} \\\\\n",
"\\end{array}\\right. \\\\\n",
"\\cos(2\\pi f_0 t) \\,&\\circ\\!\\!-\\!\\!\\bullet\\, \\left\\{\\begin{array}{rcl}\n",
" \\frac{1}{2} && f \\in \\{-f_0, f_0\\} \\\\\n",
" 0 &:& \\text{sonst} \\\\\n",
" 0 &:& \\text{otherwise} \\\\\n",
"\\end{array}\\right. \\\\\n",
"\\end{align*}\n",
"```\n",
"\n",
"Damit haben wir jeweils zwei Peaks.\n",
"Wir können Sinus und Cosinus aber geschickt kombinieren so dass sich einer davon aufhebt:\n",
"Wenn wir den Sinus mit $i$ multiplizieren bekommen wir:\n",
"That is two peaks each\n",
"We can however combine sine and cosine so that one of them cancels out:\n",
"If we multiply the sine term with $i$, we get:\n",
"```{math}\n",
"i \\cdot \\sin(2\\pi f_0 t) \\,&\\circ\\!\\!-\\!\\!\\bullet\\, \\left\\{\\begin{array}{rcl}\n",
" i \\cdot \\frac{1}{2}i = -\\frac{1}{2} && f = -f_0 \\\\\n",
" i \\cdot -\\frac{1}{2}i = \\frac{1}{2} &:& f = f_0 \\\\\n",
" 0 &:& \\text{sonst} \\\\\n",
" 0 &:& \\text{otherwise} \\\\\n",
"\\end{array}\\right.\n",
"```\n",
"Und wenn wir jetzt noch den Cosinus addieren:\n",
"And if we now add the cosine term:\n",
"```{math}\n",
"\\cos(2\\pi f_0 t) + i\\sin(2\\pi f_0 t) \\,&\\circ\\!\\!-\\!\\!\\bullet\\, \\left\\{\\begin{array}{rcl}\n",
" \\frac{1}{2} -\\frac{1}{2} = 0 && f = -f_0 \\\\\n",
" \\frac{1}{2} + \\frac{1}{2} = 1 &:& f = f_0 \\\\\n",
" 0 &:& \\text{sonst} \\\\\n",
" 0 &:& \\text{otherwise} \\\\\n",
"\\end{array}\\right.\n",
"```\n",
"Damit haben wir genau den Peak bei $f_0$.\n",
"This yields exactly one peak at $f_0$.\n",
"\n",
"```{note}\n",
"Den Ausdruck $\\cos(x) + i\\sin(x)$ habt ihr vielleicht schon mal in der [Eulerschen Formel](https://de.wikipedia.org/wiki/Eulersche_Formel) gesehen.\n",
"The expression $\\cos(x) + i\\sin(x)$ may be familliar from [Euler's formula](https://en.wikipedia.org/wiki/Euler's_formula).\n",
"```\n",
"\n",
"Um den Frequenz-Shift durchzuführen, können wir also einfach in der Zeit-Domain (= auf den Samples) mit dieser Funktion multiplizieren.\n",
"Dazu gehen wir so vor:\n",
"- Für jedes Sample $s$ berechnen wir den aktuellen Zeitpunkt $t$.\n",
" Durch die Abtastrate $f_S = 16640\\text{Hz}$ wissen wir, dass der Zeitabstand zwischen zwei Samples genau $1/16640\\text{s}$ ist.\n",
"- Dann berechnen wir $\\cos(2\\pi \\cdot -2400 \\cdot t) + i \\cdot \\sin(2\\pi \\cdot -2400 \\cdot t)$ und erhalten einen komplexen Wert $a + bi$.\n",
"- Diesen multiplizieren wir mit $s$ und erhalten $a \\cdot s + (b \\cdot s)i$ und speichern ihn als neues, komplexes Sample.\n",
"To perform the frequency shift, we can simply multiply in the time domain (= each sample) with this function.\n",
"We do this as follows:\n",
"- For every sample $s$, we calculate the current time $t$.\n",
" Since the sampling rate $f_S$ equals $16640\\text{Hz}$ we know that the duration in between two samples is exactly $1/16640\\text{s}$.\n",
"- We then calculate $\\cos(2\\pi \\cdot -2400 \\cdot t) + i \\cdot \\sin(2\\pi \\cdot -2400 \\cdot t)$ and obtain a complex result $a + bi$.\n",
"- We multiply $s$ with this, resulting in $a \\cdot s + (b \\cdot s)i$, which we save as our new (complex) sample.\n",
"\n",
"```{note}\n",
"Für dieses Projekt reicht es theoretisch aus, komplexe Zahlen als Paare von jeweils zwei reelle Zahlen zu speichern.\n",
"In manchen Programmiersprachen gibt es aber auch explizite Typen, um komplexe Zahlen darzustellen, z.B. [std::complex](https://en.cppreference.com/w/cpp/numeric/complex) in C++.\n",
"In Python kann man sie sogar einfach mit `a + b * 1j` erzeugen.\n",
"For this project, it is sufficient to represent complex numbers as pairs of two real numbers.\n",
"But in some programming languages, there are explicit types for complex numbers, e.g., [std::complex](https://en.cppreference.com/w/cpp/numeric/complex) in C++.\n",
"In Python, you can simply use `a + b * 1j`.\n",
"```"
]
}

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@
"id": "f611b5a0",
"metadata": {},
"source": [
"# Synchronisierung"
"# Synchronization"
]
},
{
@@ -13,45 +13,45 @@
"id": "0c5468d3",
"metadata": {},
"source": [
"Aktuell sind die Samples noch als komplexe Zahlen dargestellt.\n",
"Um weiter mit ihnen arbeiten zu können, nehmen wir jeweils den Betrag:\n",
"Currently, the samples are still represented as complex numbers.\n",
"To continue working with them, we convert them to their magnitudes:\n",
"```{math}\n",
"s = a + bi \\mapsto |s| = \\sqrt{a^2 + b^2}\n",
"```\n",
"\n",
"Hier eine Visualisierung des soweit verarbeiteten Signals:\n",
"Here is a visualization of the signal as processed so far:\n",
"```{figure} img/reference/filtered_full_scaled.webp\n",
"---\n",
"name: fig:filtered_full_scaled.en\n",
"---\n",
"Gefiltertes Signal.\n",
"Wir sehen dass die Sync-Streifen gebogen sind.\n",
"Filtered signal.\n",
"We observe that the sync lines are bent.\n",
"```\n",
"\n",
"Die Sync-Patterns markieren jeweils den Anfang einer Zeile.\n",
"Um das Bild \"gerade zu ziehen\" müssen wir sie finden.\n",
"The sync patterns mark the beginning of each line.\n",
"To \"straighten out\" the image, we have to find them.\n",
"\n",
"Dazu gehen wir so vor:\n",
"- Sync A hat das Pattern `000011001100110011001100110011000000000`.\n",
" Wir haben 4 Samples pro Pixel, also auch pro 0/1 im Pattern.\n",
" Dieses Pattern wollen wir suchen.\n",
" Das funktioniert hier am besten, wenn wir sowohl das Pattern als auch die Samples in den Wertebereich zwischen $-1$ und $1$ skalieren.\n",
" Für jede `0` im Pattern nehmen wir also 4 mal die $-1$, und für jede `1` nehmen wir 4 mal die $1$.\n",
" Diese Sequenz von 39 * 4 = 156 Werten speichern wir als $p$.\n",
"- Jetzt betrachten wir Blöcke $x$ von 8320 Samples.\n",
" Wir finden Minimum $x_{min}$ und Maximum $x_{max}$ im Block, und skalieren so dass das Minimum bei $-1$ ist und das Maximum bei $1$:\n",
"We proceed as follows:\n",
"- Sync A has the pattern `000011001100110011001100110011000000000`.\n",
" We have 4 samples per pixel, and thus also per 0/1 in the pattern.\n",
" We want to find this pattern.\n",
" This is easiest if we scale both the pattern and our samples into the range between $-1$ and $1$.\n",
" For every `0` in the pattern, we take four $-1$s, and for every `1` we take four $1$s.\n",
" We save this sequence of 39 * 4 = 156 values as $p$.\n",
"- We now look at blocks $x$ of 8320 samples.\n",
" We find the minimum value $x_{min}$ and the maximum value $x_{max}$ within the block, and scale it such that the minimum is at $-1$ and the maximum at $1$:\n",
" ```{math}\n",
" x[n] \\mapsto -1 + 2 \\cdot \\frac{x[n] - x_{min}}{x_{max} - x_{min}}\n",
" ```\n",
"- Wir wollen die Position finden, die \"am meisten\" mit dem Pattern übereinstimmt.\n",
" Also iterieren wir über alle Startpositionen $i$ im Block, 0 bis (8320-156).\n",
" Für jede davon berechnen wir\n",
"- We want to find the position that best matches the pattern.\n",
" To do this, we iterate over all positions $i$ in the block from 0 to (8320-156).\n",
" For each position, we compute the *correlation*\n",
" ```{math}\n",
" z = \\sum_{n=0}^{156} x[i + n] \\cdot p[n]\n",
" ```\n",
" Die Position bei der $z$ am größten ist, ist die mit der besten Übereinstimmung.\n",
"- So können wir für jede Zeile die Anfangsposition finden.\n",
" Von dort aus gehen wir in Viererschritten über die Samples, um die Pixelwerte für die Zeile zu erhalten."
" The position with the highest $z$ value is the one that matches best.\n",
"- This way, we can find the starting position of each line.\n",
" From there, we can take every fourth sample to get the pixel values for this line."
]
}
],

View File

@@ -5,7 +5,7 @@
"id": "af12be1c",
"metadata": {},
"source": [
"# Ausgabe"
"# Output"
]
},
{
@@ -13,30 +13,30 @@
"id": "8de61e70",
"metadata": {},
"source": [
"Wir haben jetzt Werte für die Pixel des Bildes, aber sie sind noch nicht im richtigen Bereich:\n",
"Sie müssen am Ende nämlich zwischen 0 und 255 liegen.\n",
"We now have values for each pixel in the image, but these values are not yet in the correct range.\n",
"At the end of the day, we want them to be between 0 and 255.\n",
"\n",
"Um sie zu richtig zu skalieren, verwenden wir den \"Space and Marker\" Teil des Bildformats:\n",
"To scale them correctly, we use the \"space and marker\" part of the frame:\n",
"\n",
"```{figure} img/apt_frame_format.webp\n",
"---\n",
"name: fig:frame_format_output.en\n",
"---\n",
"Wieder das APT Bildformat.\n",
"Nach Sync A (39 Pixel breit) folgt ein 47 Pixel breiter Space mit schwarzen Pixeln.\n",
"Analog dazu sind in der zweiten Hälfte des Bildes der ebenfalls 39 Pixel breite Sync B und ein 47 Pixel breiter Space mit weißen Pixeln.\n",
"The APT frame format again.\n",
"Sync A (39 pixels) is followed by a space of 47 black pixels.\n",
"Similarly, sync B (39 pixel) in the second half is followed by a space of 47 white pixels.\n",
"```\n",
"\n",
"Der Sync A folgende Space hat (bis auf den Minute Marker) weiße Pixel, und der Space nach Sync B schwarze.\n",
"Die Minute Marker ignorieren wir hier einfach :)\n",
"The space following sync A has black pixels (except for the minute markers), and the space following sync b has white pixels.\n",
"We just ignore the minute markers :)\n",
"\n",
"Um die Pixelwerte in den richtigen Bereich zu skalieren können wir so vorgehen:\n",
"- Wir berechnen das Schwarz-Level $v_b$ indem wir den Durchschnitt (oder den [Median](https://de.wikipedia.org/wiki/Median)) aller Pixelwerte im ersten Space nehmen.\n",
"- Das gleiche tun wir für den Weiß-Level $v_w$ mit dem zweiten Space.\n",
"- Jetzt bilden wir jeden Pixelwert $v$ auf $(v - v_b) / (v_w - v_b)$ ab.\n",
"- Damit sind die Werte zwischen $v_b$ und $v_w$ in den Bereich zwischen 0 und 1 gewandert.\n",
"- Alle Werte kleiner als 0 oder größer als 1 werden auf 0 bzw. 1 begrenzt.\n",
"- Jetzt müssen wir nur noch jeden Wert mit 255 multiplizieren."
"We can scale the pixel values into the correct range like this:\n",
"- We calculate the black level $v_b$ by computing the average (or [median](https://en.wikipedia.org/wiki/Median)) value of all pixels in the first space.\n",
"- We do the same with the second space to get the white level $v_w$.\n",
"- We now map every pixel value $v$ to $(v - v_b) / (v_w - v_b)$.\n",
"- This has moved all values between $v_b$ and $v_w$ into the range between 0 and 1.\n",
"- All values outside this range are clamped to 0 or 1 respectively.\n",
"- Finally, we have to multiply each value with 255."
]
},
{
@@ -45,22 +45,22 @@
"metadata": {},
"source": [
"(pgm-format)=\n",
"## PGM-Format\n",
"## PGM format\n",
"\n",
"```{note}\n",
"Wer möchte, kann natürlich gerne ein anderes Format benutzen, oder eine Bibliothek wie [Pillow](https://python-pillow.org/) oder [SDL](https://www.libsdl.org/).\n",
"Feel free to use a different format, or an image library like [Pillow](https://python-pillow.org/) or [SDL](https://www.libsdl.org/).\n",
"```\n",
"\n",
"Um das fertige Bild in eine Datei zu schreiben, können wir zum Beispiel dieses sehr einfache Dateiformat benutzen:\n",
"To write the final image into a file, we can use this very simple file format:\n",
"\n",
"```{admonition} Format\n",
"Die erste Zeile enthält den String `P2`.\n",
"The first line contains the string `P2`.\n",
"\n",
"Die zweite Zeile enthält zwei positive Integer $w$ und $h$, die Breite $(w)$ und he $(h)$ des Bildes $(w = 2080, h \\approx 1400)$.\n",
"The second line contains two positive integers $w$ and $h$, describing the width $(w)$ and height $(h)$ of the image $(w = 2080, h \\approx 1400)$.\n",
"\n",
"Die dritte Zeile enthält einen positiven Integer $v_{max} = 255$.\n",
"The third line contains the positive integer $v_{max} = 255$.\n",
"\n",
"Danach folgen $h$ Zeilen mit jeweils $w$ Pixelwerten $v$ als Integer $(0 \\leq v \\leq v_{max})$.\n",
"Afterwards, $h$ lines follow, containing $w$ integer pixel values $v$ $(0 \\leq v \\leq v_{max})$.\n",
"```"
]
},
@@ -69,7 +69,7 @@
"id": "1ab1434e",
"metadata": {},
"source": [
"### Beispiel\n",
"### Example\n",
"\n",
"```\n",
"P2\n",
@@ -91,8 +91,8 @@
"metadata": {},
"source": [
"```{note}\n",
"Das PGM-Format ist nur für Bilder in Graustufen, aber es gibt auch z.B. das PPM-Format für farbige Bilder.\n",
"[Hier](https://en.wikipedia.org/wiki/Netpbm) könnt ihr mehr dazu lesen.\n",
"The PGM format only supports grayscale images, but there is also e.g. the PPM format for colored images.\n",
"You can read more about it [here](https://en.wikipedia.org/wiki/Netpbm).\n",
"```"
]
},
@@ -101,7 +101,7 @@
"id": "d16219b2",
"metadata": {},
"source": [
"Wenn wir unsere Pixelwerte so in eine Datei mit `.pgm`-Endung speichern, können wir sie mit einem Grafikprogramm wie z.B. [GIMP](https://www.gimp.org/) anschauen."
"After saving the pixel values in such a file with a `.pgm` extension, we can look at them using a graphics program like [GIMP](https://www.gimp.org/)."
]
}
],