<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="sv">
	<id>http://wiki.sikvall.se/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Anders</id>
	<title>Täpp-Anders - Användarbidrag [sv]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.sikvall.se/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Anders"/>
	<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php/Special:Bidrag/Anders"/>
	<updated>2026-09-05T09:57:30Z</updated>
	<subtitle>Användarbidrag</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Squashfs&amp;diff=1946</id>
		<title>Squashfs</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Squashfs&amp;diff=1946"/>
		<updated>2026-08-24T17:29:28Z</updated>

		<summary type="html">&lt;p&gt;Anders: Skapade sidan med &amp;#039;Squashfs är ett komprimerat filsystem som kan direkt monteras i Linux eller packas upp i RAM osv. Det skapas från en katalogstruktur med dina filer i och fungerar därför också utmärkt som ett sätt att ta backup.  = Backup med squashfs =  &amp;lt;pre&amp;gt; $ mksquashfs &amp;lt;directory&amp;gt; &amp;lt;name&amp;gt; &amp;lt;/pre&amp;gt;  Example:  &amp;lt;pre&amp;gt; $ mksquashfs workdir workdir.squashfs Parallel mksquashfs: Using 8 processors Creating 4.0 filesystem on workdir.squashfs, block size 131072. [=========================...&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Squashfs är ett komprimerat filsystem som kan direkt monteras i Linux eller packas upp i RAM osv. Det skapas från en katalogstruktur med dina filer i och fungerar därför också utmärkt som ett sätt att ta backup.&lt;br /&gt;
&lt;br /&gt;
= Backup med squashfs =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ mksquashfs &amp;lt;directory&amp;gt; &amp;lt;name&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ mksquashfs workdir workdir.squashfs&lt;br /&gt;
Parallel mksquashfs: Using 8 processors&lt;br /&gt;
Creating 4.0 filesystem on workdir.squashfs, block size 131072.&lt;br /&gt;
[=================================================================|] 91/91 100%&lt;br /&gt;
&lt;br /&gt;
Exportable Squashfs 4.0 filesystem, gzip compressed, data block size 131072&lt;br /&gt;
        compressed data, compressed metadata, compressed fragments,&lt;br /&gt;
        compressed xattrs, compressed ids&lt;br /&gt;
        duplicates are removed&lt;br /&gt;
Filesystem size 202.88 Kbytes (0.20 Mbytes)&lt;br /&gt;
        3.14% of uncompressed filesystem size (6456.66 Kbytes)&lt;br /&gt;
Inode table size 794 bytes (0.78 Kbytes)&lt;br /&gt;
        31.68% of uncompressed inode table size (2506 bytes)&lt;br /&gt;
Directory table size 990 bytes (0.97 Kbytes)&lt;br /&gt;
        56.73% of uncompressed directory table size (1745 bytes)&lt;br /&gt;
Number of duplicate files found 4&lt;br /&gt;
Number of inodes 72&lt;br /&gt;
Number of files 44&lt;br /&gt;
Number of fragments 1&lt;br /&gt;
Number of symbolic links 0&lt;br /&gt;
Number of device nodes 0&lt;br /&gt;
Number of fifo nodes 0&lt;br /&gt;
Number of socket nodes 0&lt;br /&gt;
Number of directories 28&lt;br /&gt;
Number of ids (unique uids + gids) 1&lt;br /&gt;
Number of uids 1&lt;br /&gt;
        anders (1000)&lt;br /&gt;
Number of gids 1&lt;br /&gt;
        anders (1000)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den fina grejen här är att mksquashfs använder alla propparna du har för att köra multitrådat, den komprimerar relativt väl och den är faktiskt ganska snabb. När du behöver plocka ut något ur den kan du direkt montera arkivet och sedan gå runt i det som vilket filsystem som helst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ mkdir mnt&lt;br /&gt;
$ sudo mount workdir.squashfs mnt&lt;br /&gt;
$ cd mnt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=AI-rymdv%C3%A4der&amp;diff=1945</id>
		<title>AI-rymdväder</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=AI-rymdv%C3%A4der&amp;diff=1945"/>
		<updated>2026-08-05T23:20:44Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Rymdväder för radioutbredning på kort- och mellanvåg&lt;br /&gt;
&lt;br /&gt;
## Bakgrund&lt;br /&gt;
&lt;br /&gt;
Rymdväder karaktäriseras av flera olika parametrar som solfläcksindex,&lt;br /&gt;
K- och A-värden med mera.&lt;br /&gt;
&lt;br /&gt;
### Primära källor&lt;br /&gt;
&lt;br /&gt;
Dessa tillhandahålls på olika ställen på nätet som dessa som listas i viktighetsordning:&lt;br /&gt;
&lt;br /&gt;
 * NOAA https://www.spaceweather.gov/&lt;br /&gt;
 * Tamita Skov https://www.spaceweatherwoman.com/&lt;br /&gt;
 * LRF / IRF https://www2.irf.se/Observatory/?link=Magnetometers&lt;br /&gt;
 * Swedish Space weather https://spaceweather.irf.se/&lt;br /&gt;
 &lt;br /&gt;
### Detaljnivå&lt;br /&gt;
&lt;br /&gt;
Kortfattad, tabellform före brödtext.&lt;br /&gt;
&lt;br /&gt;
### Stationsinformation&lt;br /&gt;
&lt;br /&gt;
 * Radiostationen har alla amtörradioband mellan 160–6 meter.&lt;br /&gt;
 * Antennen är en änmatad trådantenn med 1:9 balun, icke-resonant så kallad EFRW.&lt;br /&gt;
 * Stationens maximala uteffekt är 100 W RMS.&lt;br /&gt;
&lt;br /&gt;
### Moder&lt;br /&gt;
&lt;br /&gt;
Fokusera på följande moder:&lt;br /&gt;
&lt;br /&gt;
 * SSB&lt;br /&gt;
 * CW&lt;br /&gt;
&lt;br /&gt;
Undvik följande moder:&lt;br /&gt;
&lt;br /&gt;
* Digitala&lt;br /&gt;
&lt;br /&gt;
## Sammanställning av rådande rymdväder samt upp till 4 dagar framåt&lt;br /&gt;
&lt;br /&gt;
Gör en sammanställning över dessa källor och andra källor som du&lt;br /&gt;
hittar på nätet med inriktning på att:&lt;br /&gt;
&lt;br /&gt;
### För de kommande 4 dagarna&lt;br /&gt;
&lt;br /&gt;
Prediktera utfallet för att använda frekvensbanden som listas nedan&lt;br /&gt;
från Karlholmsbruk i Sverige.&lt;br /&gt;
&lt;br /&gt;
 - 160 meter&lt;br /&gt;
 - 80 meter&lt;br /&gt;
 - 40 meter&lt;br /&gt;
 - 60 meter&lt;br /&gt;
 - 20 meter&lt;br /&gt;
 - 17 meter&lt;br /&gt;
 - 15 meter&lt;br /&gt;
 - 12 meter&lt;br /&gt;
 - 10 meter&lt;br /&gt;
&lt;br /&gt;
* Gör sammanställningen i tabellform. Tabellen ska innehålla: Frekvensband, Dagtid, Nattetid, Prognos 4 dagar&lt;br /&gt;
&lt;br /&gt;
* Gör också en tydlig indikator över geomagnetisk storm som har följande skala:&lt;br /&gt;
&lt;br /&gt;
TYST - OROLIGT - LÄTT STORM - MEDEL STORM - SVÅR STORM - ORKAN&lt;br /&gt;
&lt;br /&gt;
### Sporadiskt Es&lt;br /&gt;
&lt;br /&gt;
Om det finns möjlighet till utbredning via sporadiskt Es försök att&lt;br /&gt;
prediktera tider och möjligheter från Karlholmsbruk.&lt;br /&gt;
&lt;br /&gt;
### Notera övriga risker&lt;br /&gt;
&lt;br /&gt;
Kortfattat och i tabellform:&lt;br /&gt;
&lt;br /&gt;
* CME i procent över de kommande 4 dagarna&lt;br /&gt;
* Solfackla (flare) över de kommande 4 dagarna&lt;br /&gt;
&lt;br /&gt;
# Slut&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=AI-rymdv%C3%A4der&amp;diff=1944</id>
		<title>AI-rymdväder</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=AI-rymdv%C3%A4der&amp;diff=1944"/>
		<updated>2026-08-05T23:19:30Z</updated>

		<summary type="html">&lt;p&gt;Anders: Skapade sidan med &amp;#039;&amp;lt;pre&amp;gt;  # Rymdväder för radioutbredning på kort- och mellanvåg  ## Bakgrund  Rymdväder karaktäriseras av flera olika parametrar som solfläcksindex, K- och A-värden med mera.  ### Primära källor  Dessa tillhandahålls på olika ställen på nätet som dessa som listas i viktighetsordning:   * NOAA https://www.spaceweather.gov/  * Tamita Skov https://www.spaceweatherwoman.com/  * LRF / IRF https://www2.irf.se/Observatory/?link=Magnetometers  * Swedish Space weathe...&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Rymdväder för radioutbredning på kort- och mellanvåg&lt;br /&gt;
&lt;br /&gt;
## Bakgrund&lt;br /&gt;
&lt;br /&gt;
Rymdväder karaktäriseras av flera olika parametrar som solfläcksindex,&lt;br /&gt;
K- och A-värden med mera.&lt;br /&gt;
&lt;br /&gt;
### Primära källor&lt;br /&gt;
&lt;br /&gt;
Dessa tillhandahålls på olika ställen på nätet som dessa som listas i viktighetsordning:&lt;br /&gt;
&lt;br /&gt;
 * NOAA https://www.spaceweather.gov/&lt;br /&gt;
 * Tamita Skov https://www.spaceweatherwoman.com/&lt;br /&gt;
 * LRF / IRF https://www2.irf.se/Observatory/?link=Magnetometers&lt;br /&gt;
 * Swedish Space weather https://spaceweather.irf.se/&lt;br /&gt;
 &lt;br /&gt;
### Detaljnivå&lt;br /&gt;
&lt;br /&gt;
Kortfattad, tabellform före brödtext.&lt;br /&gt;
&lt;br /&gt;
### Stationsinformation&lt;br /&gt;
&lt;br /&gt;
 * Radiostationen har alla amtörradioband mellan 160–6 meter.&lt;br /&gt;
 * Antennen är en änmatad trådantenn med 1:9 balun, icke-resonant så kallad EFRW.&lt;br /&gt;
 * Stationens maximala uteffekt är 100 W RMS.&lt;br /&gt;
&lt;br /&gt;
### Moder&lt;br /&gt;
&lt;br /&gt;
Fokusera på följande moder:&lt;br /&gt;
&lt;br /&gt;
 * SSB&lt;br /&gt;
 * CW&lt;br /&gt;
&lt;br /&gt;
Undvik följande moder:&lt;br /&gt;
&lt;br /&gt;
* Digitala&lt;br /&gt;
&lt;br /&gt;
## Sammanställning av rådande rymdväder samt upp till 4 dagar framåt&lt;br /&gt;
&lt;br /&gt;
Gör en sammanställning över dessa källor och andra källor som du&lt;br /&gt;
hittar på nätet med inriktning på att:&lt;br /&gt;
&lt;br /&gt;
### För de kommande 4 dagarna&lt;br /&gt;
&lt;br /&gt;
Prediktera utfallet för att använda frekvensbanden som listas nedan&lt;br /&gt;
från Karlholmsbruk i Sverige.&lt;br /&gt;
&lt;br /&gt;
 - 160 meter&lt;br /&gt;
 - 80 meter&lt;br /&gt;
 - 40 meter&lt;br /&gt;
 - 60 meter&lt;br /&gt;
 - 20 meter&lt;br /&gt;
 - 17 meter&lt;br /&gt;
 - 15 meter&lt;br /&gt;
 - 12 meter&lt;br /&gt;
 - 10 meter&lt;br /&gt;
&lt;br /&gt;
* Gör sammanställningen i tabellform. Tabellen ska innehålla: Frekvensband, Dagtid, Nattetid, Prognos 4 dagar&lt;br /&gt;
&lt;br /&gt;
* Gör också en tydlig indikator över geomagnetisk storm som har följande skala:&lt;br /&gt;
&lt;br /&gt;
TYST - OROLIGT - LÄTT STORM - MEDEL STORM - SVÅR STORM - ORKAN&lt;br /&gt;
&lt;br /&gt;
### Sporadiskt Es&lt;br /&gt;
&lt;br /&gt;
Om det finns möjlighet till utbredning via sporadiskt Es försök att&lt;br /&gt;
prediktera tider och möjligheter från Karlholmsbruk.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Rymdv%C3%A4der&amp;diff=1943</id>
		<title>Rymdväder</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Rymdv%C3%A4der&amp;diff=1943"/>
		<updated>2026-08-05T23:19:04Z</updated>

		<summary type="html">&lt;p&gt;Anders: Skapade sidan med &amp;#039;Diverse saker om rymdväder som är bra att ha koll på.  * Fil för AI-predikering om rymdväder AI-rymdväder&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Diverse saker om rymdväder som är bra att ha koll på.&lt;br /&gt;
&lt;br /&gt;
* Fil för AI-predikering om rymdväder [[AI-rymdväder]]&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Principia&amp;diff=1942</id>
		<title>Principia</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Principia&amp;diff=1942"/>
		<updated>2026-07-31T09:06:29Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Parkinsons lag&lt;br /&gt;
* Hofstadters lag&lt;br /&gt;
* Hanlons rakkniv&lt;br /&gt;
* Paretos princip&lt;br /&gt;
* Peters princip&lt;br /&gt;
* Hicks lag&lt;br /&gt;
* Goodharts lag&lt;br /&gt;
* Dunning-Kruger effekt&lt;br /&gt;
* Occams rakkniv&lt;br /&gt;
* Chestertons staket&lt;br /&gt;
* Brooks lag&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Principia&amp;diff=1941</id>
		<title>Principia</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Principia&amp;diff=1941"/>
		<updated>2026-07-31T09:06:02Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Parkinsons lag&lt;br /&gt;
&lt;br /&gt;
Arbetets omfattning expanderar till att fylla tiden avsatt för arbetet. &lt;br /&gt;
&lt;br /&gt;
* Hofstadters lag&lt;br /&gt;
* Hanlons rakkniv&lt;br /&gt;
* Paretos princip&lt;br /&gt;
* Peters princip&lt;br /&gt;
* Hicks lag&lt;br /&gt;
* Goodharts lag&lt;br /&gt;
* Dunning-Kruger effekt&lt;br /&gt;
* Occams rakkniv&lt;br /&gt;
* Chestertons staket&lt;br /&gt;
* Brooks lag&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Principia&amp;diff=1940</id>
		<title>Principia</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Principia&amp;diff=1940"/>
		<updated>2026-07-31T09:05:20Z</updated>

		<summary type="html">&lt;p&gt;Anders: Skapade sidan med &amp;#039;* Parkinsons lag * Hofstadters lag * Hanlons rakkniv * Paretos princip * Peters princip * Hicks lag * Goodharts lag * Dunning-Kruger effekt * Occams rakkniv * Chestertons staket * Brooks lag&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Parkinsons lag&lt;br /&gt;
* Hofstadters lag&lt;br /&gt;
* Hanlons rakkniv&lt;br /&gt;
* Paretos princip&lt;br /&gt;
* Peters princip&lt;br /&gt;
* Hicks lag&lt;br /&gt;
* Goodharts lag&lt;br /&gt;
* Dunning-Kruger effekt&lt;br /&gt;
* Occams rakkniv&lt;br /&gt;
* Chestertons staket&lt;br /&gt;
* Brooks lag&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/TSS&amp;diff=1939</id>
		<title>Racket/TSS</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/TSS&amp;diff=1939"/>
		<updated>2026-07-11T15:17:40Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;;;;;;;&lt;br /&gt;
;; Anders Sikvall Sommarhack 2026&lt;br /&gt;
;; 2026-07-09 &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
;;&lt;br /&gt;
;; Använder &amp;quot;TrainingPeaks&amp;quot; metod för beräkningar av de olika parametrarna&lt;br /&gt;
;; baserade på Training Stress Score (TSS) som i sin ur ger de olika&lt;br /&gt;
;; måtten enligt:&lt;br /&gt;
;;&lt;br /&gt;
;; TSS -- Training stress score, belastningsmått för motionsaktiviteter&lt;br /&gt;
;; CTL -- Chronic Training Level, TSS utslaget över 6 veckors tid, viktat mot närtid&lt;br /&gt;
;; ATL -- Acute Training Level, TSS utslaget över senaste 7 dagarna&lt;br /&gt;
;; TSB -- Skillnaden mellan ATL-CTL, negativt betyder förbättring&lt;br /&gt;
&lt;br /&gt;
(require 2htdp/batch-io)&lt;br /&gt;
(require racket/date)&lt;br /&gt;
(require plot)&lt;br /&gt;
&lt;br /&gt;
;; --- Konstanter och Alfa-värden ---&lt;br /&gt;
(define ctl-alpha (/ 2 (+ 42 1)))&lt;br /&gt;
(define atl-alpha (/ 2 (+ 7 1)))&lt;br /&gt;
(define SECONDS-PER-DAY 86400)&lt;br /&gt;
&lt;br /&gt;
(define (calc-next-ewma current-tss previous-val alpha)&lt;br /&gt;
  (+ (* current-tss alpha) (* previous-val (- 1 alpha))))&lt;br /&gt;
&lt;br /&gt;
;; Datumhantering&lt;br /&gt;
(define (string-&amp;gt;date-seconds raw-str)&lt;br /&gt;
  (define clean-str (string-trim raw-str (regexp &amp;quot;[\&amp;quot;&#039;\r\n ]&amp;quot;)))&lt;br /&gt;
  (match (regexp-match #px&amp;quot;([0-9]{4})-([0-9]{2})-([0-9]{2})&amp;quot; clean-str)&lt;br /&gt;
    [(list _ year-str month-str day-str)&lt;br /&gt;
     (find-seconds 0 0 12 &lt;br /&gt;
                   (string-&amp;gt;number day-str)&lt;br /&gt;
                   (string-&amp;gt;number month-str)&lt;br /&gt;
                   (string-&amp;gt;number year-str))]&lt;br /&gt;
    [_ (error &amp;quot;Felaktigt datumformat, använd YYYY-MM-DD:&amp;quot; raw-str)]))&lt;br /&gt;
&lt;br /&gt;
(define (date-seconds-&amp;gt;string secs)&lt;br /&gt;
  (define d (seconds-&amp;gt;date secs))&lt;br /&gt;
  (format &amp;quot;~a-~a-~a&amp;quot;&lt;br /&gt;
          (date-year d)&lt;br /&gt;
          (if (&amp;lt; (date-month d) 10) (format &amp;quot;0~a&amp;quot; (date-month d)) (date-month d))&lt;br /&gt;
          (if (&amp;lt; (date-day d) 10) (format &amp;quot;0~a&amp;quot; (date-day d)) (date-day d))))&lt;br /&gt;
&lt;br /&gt;
;; --- Ackumulering och Utfyllnad ---&lt;br /&gt;
(define (fill-and-compute-metrics data-map start-secs end-secs)&lt;br /&gt;
  (let loop ([current start-secs]&lt;br /&gt;
             [prev-ctl 0.0]&lt;br /&gt;
             [prev-atl 0.0]&lt;br /&gt;
             [acc &#039;()])&lt;br /&gt;
    (if (&amp;gt; current end-secs)&lt;br /&gt;
        (reverse acc)&lt;br /&gt;
        (let* ([date-str (date-seconds-&amp;gt;string current)]&lt;br /&gt;
               [tss (hash-ref data-map date-str 0.0)]&lt;br /&gt;
               [current-ctl (calc-next-ewma tss prev-ctl ctl-alpha)]&lt;br /&gt;
               [current-atl (calc-next-ewma tss prev-atl atl-alpha)]&lt;br /&gt;
               [current-tsb (- current-ctl current-atl)])&lt;br /&gt;
          (loop (+ current SECONDS-PER-DAY)&lt;br /&gt;
                current-ctl&lt;br /&gt;
                current-atl&lt;br /&gt;
                (cons (list current date-str tss current-ctl current-atl current-tsb) acc))))))&lt;br /&gt;
&lt;br /&gt;
;; --- Plottning (Konfigurerbar storlek) ---&lt;br /&gt;
(define (plot-training-data calculated-timeline #:width w #:height h)&lt;br /&gt;
  (define-values (tss-pts ctl-pts atl-pts tsb-pts)&lt;br /&gt;
    (for/fold ([tss-acc &#039;()] [ctl-acc &#039;()] [atl-acc &#039;()] [tsb-acc &#039;()])&lt;br /&gt;
              ([day calculated-timeline])&lt;br /&gt;
      (match-let ([(list secs date tss ctl atl tsb) day])&lt;br /&gt;
        (values (cons (list secs tss) tss-acc)&lt;br /&gt;
                (cons (list secs ctl) ctl-acc)&lt;br /&gt;
                (cons (list secs atl) atl-acc)&lt;br /&gt;
                (cons (list secs tsb) tsb-acc)))))&lt;br /&gt;
&lt;br /&gt;
  (parameterize ([plot-x-ticks (date-ticks)]&lt;br /&gt;
                 [plot-x-label &amp;quot;Datum&amp;quot;]&lt;br /&gt;
                 [plot-y-label &amp;quot;Värde (TSS / CTL / ATL / TSB)&amp;quot;]&lt;br /&gt;
                 [plot-width w]&lt;br /&gt;
                 [plot-height h])&lt;br /&gt;
    &lt;br /&gt;
    (plot (list (lines (reverse tss-pts) #:color &amp;quot;lightgray&amp;quot; #:width 1 #:label &amp;quot;TSS&amp;quot;)&lt;br /&gt;
                (lines (reverse ctl-pts) #:color &amp;quot;blue&amp;quot; #:width 2 #:label &amp;quot;CTL (Kondition)&amp;quot;)&lt;br /&gt;
                (lines (reverse atl-pts) #:color &amp;quot;red&amp;quot; #:width 2 #:label &amp;quot;ATL (Trötthet)&amp;quot;)&lt;br /&gt;
                (lines (reverse tsb-pts) #:color &amp;quot;forestgreen&amp;quot; #:width 2 #:style &#039;long-dash #:label &amp;quot;TSB (Form)&amp;quot;))&lt;br /&gt;
          #:title &amp;quot;Träningsbelastning&amp;quot;&lt;br /&gt;
          #:legend-anchor &#039;top-left)))&lt;br /&gt;
&lt;br /&gt;
;; --- Huvudfunktion ---&lt;br /&gt;
(define (process-training-data input-file output-file #:plot-width [w 1000] #:plot-height [h 600])&lt;br /&gt;
  (define raw-lines (read-csv-file input-file))&lt;br /&gt;
  &lt;br /&gt;
  (define data-lines &lt;br /&gt;
    (filter (lambda (row) &lt;br /&gt;
              (and (not (null? row)) &lt;br /&gt;
                   (regexp-match? #px&amp;quot;[0-9]&amp;quot; (car row))&lt;br /&gt;
                   (not (string-prefix? (string-trim (car row)) &amp;quot;Datum&amp;quot;))))&lt;br /&gt;
            raw-lines))&lt;br /&gt;
  &lt;br /&gt;
  (define data-map&lt;br /&gt;
    (for/hash ([row data-lines])&lt;br /&gt;
      (define clean-date (date-seconds-&amp;gt;string (string-&amp;gt;date-seconds (car row))))&lt;br /&gt;
      (define raw-tss (string-&amp;gt;number (string-trim (cadr row) (regexp &amp;quot;[\&amp;quot;&#039;\r\n ]&amp;quot;))))&lt;br /&gt;
      (define clean-tss (if raw-tss raw-tss 0.0))&lt;br /&gt;
      (values clean-date clean-tss)))&lt;br /&gt;
  &lt;br /&gt;
  (define sorted-secs &lt;br /&gt;
    (sort (map string-&amp;gt;date-seconds (hash-keys data-map)) &amp;lt;))&lt;br /&gt;
  &lt;br /&gt;
  (when (null? sorted-secs)&lt;br /&gt;
    (error &amp;quot;Ingen giltig träningsdata hittades i filen.&amp;quot;))&lt;br /&gt;
  &lt;br /&gt;
  (define start-secs (car sorted-secs))&lt;br /&gt;
  (define end-secs (last sorted-secs))&lt;br /&gt;
  &lt;br /&gt;
  (define calculated-timeline &lt;br /&gt;
    (fill-and-compute-metrics data-map start-secs end-secs))&lt;br /&gt;
  &lt;br /&gt;
  (define header &#039;(&amp;quot;Datum&amp;quot; &amp;quot;TSS&amp;quot; &amp;quot;CTL&amp;quot; &amp;quot;ATL&amp;quot; &amp;quot;TSB&amp;quot;))&lt;br /&gt;
  (define output-rows&lt;br /&gt;
    (cons header&lt;br /&gt;
          (map (lambda (day-data)&lt;br /&gt;
                 (match-let ([(list secs date tss ctl atl tsb) day-data])&lt;br /&gt;
                   (list date&lt;br /&gt;
                         (number-&amp;gt;string (exact-&amp;gt;inexact tss))&lt;br /&gt;
                         (real-&amp;gt;decimal-string ctl 1)&lt;br /&gt;
                         (real-&amp;gt;decimal-string atl 1)&lt;br /&gt;
                         (real-&amp;gt;decimal-string tsb 1))))&lt;br /&gt;
               calculated-timeline)))&lt;br /&gt;
  &lt;br /&gt;
  (write-file output-file &lt;br /&gt;
              (string-join &lt;br /&gt;
               (map (lambda (row) (string-join row &amp;quot;,&amp;quot;)) output-rows)&lt;br /&gt;
               &amp;quot;\n&amp;quot;))&lt;br /&gt;
  (printf &amp;quot;Klart! Data har fyllts i dag för dag och skrivits till ~a\n&amp;quot; output-file)&lt;br /&gt;
  &lt;br /&gt;
  (plot-training-data calculated-timeline #:width w #:height h))&lt;br /&gt;
&lt;br /&gt;
;; --- KÖR PROGRAMMET ---&lt;br /&gt;
;; Justera värdena för #:plot-width och #:plot-height nedan för att ändra storlek.&lt;br /&gt;
(process-training-data &amp;quot;training.csv&amp;quot; &amp;quot;training_calculated.csv&amp;quot;&lt;br /&gt;
                       #:plot-width 1800&lt;br /&gt;
                       #:plot-height 800)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/TSS&amp;diff=1938</id>
		<title>Racket/TSS</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/TSS&amp;diff=1938"/>
		<updated>2026-07-09T19:32:59Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;;;;;;;&lt;br /&gt;
;; Anders Sikvall Sommarhack 2026&lt;br /&gt;
;; 2026-07-09 &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
;;&lt;br /&gt;
;; Använder &amp;quot;TrainingPeaks&amp;quot; metod för beräkningar av de olika parametrarna&lt;br /&gt;
;; baserade på Training Stress Score (TSS) som i sin ur ger de olika&lt;br /&gt;
;; måtten enligt:&lt;br /&gt;
;;&lt;br /&gt;
;; TSS -- Training stress score, belastningsmått för motionsaktiviteter&lt;br /&gt;
;; CTL -- Chronic Training Level, TSS utslaget över 6 veckors tid, viktat mot närtid&lt;br /&gt;
;; ATL -- Acute Training Level, TSS utslaget över senaste 7 dagarna&lt;br /&gt;
;; TSB -- Skillnaden mellan ATL-CTL, negativt betyder förbättring&lt;br /&gt;
&lt;br /&gt;
(require 2htdp/batch-io)&lt;br /&gt;
(require racket/date)&lt;br /&gt;
(require plot)&lt;br /&gt;
&lt;br /&gt;
;; --- Konstanter och Alfa-värden ---&lt;br /&gt;
(define ctl-alpha (/ 2 (+ 42 1)))&lt;br /&gt;
(define atl-alpha (/ 2 (+ 7 1)))&lt;br /&gt;
(define SECONDS-PER-DAY 86400)&lt;br /&gt;
&lt;br /&gt;
(define (calc-next-ewma current-tss previous-val alpha)&lt;br /&gt;
  (+ (* current-tss alpha) (* previous-val (- 1 alpha))))&lt;br /&gt;
&lt;br /&gt;
;; Datumhantering&lt;br /&gt;
(define (string-&amp;gt;date-seconds raw-str)&lt;br /&gt;
  (define clean-str (string-trim raw-str (regexp &amp;quot;[\&amp;quot;&#039;\r\n ]&amp;quot;)))&lt;br /&gt;
  (match (regexp-match #px&amp;quot;([0-9]{4})-([0-9]{2})-([0-9]{2})&amp;quot; clean-str)&lt;br /&gt;
    [(list _ year-str month-str day-str)&lt;br /&gt;
     (find-seconds 0 0 12 &lt;br /&gt;
                   (string-&amp;gt;number day-str)&lt;br /&gt;
                   (string-&amp;gt;number month-str)&lt;br /&gt;
                   (string-&amp;gt;number year-str))]&lt;br /&gt;
    [_ (error &amp;quot;Felaktigt datumformat, använd YYYY-MM-DD:&amp;quot; raw-str)]))&lt;br /&gt;
&lt;br /&gt;
(define (date-seconds-&amp;gt;string secs)&lt;br /&gt;
  (define d (seconds-&amp;gt;date secs))&lt;br /&gt;
  (format &amp;quot;~a-~a-~a&amp;quot;&lt;br /&gt;
          (date-year d)&lt;br /&gt;
          (if (&amp;lt; (date-month d) 10) (format &amp;quot;0~a&amp;quot; (date-month d)) (date-month d))&lt;br /&gt;
          (if (&amp;lt; (date-day d) 10) (format &amp;quot;0~a&amp;quot; (date-day d)) (date-day d))))&lt;br /&gt;
&lt;br /&gt;
;; --- Ackumulering och Utfyllnad ---&lt;br /&gt;
(define (fill-and-compute-metrics data-map start-secs end-secs)&lt;br /&gt;
  (let loop ([current start-secs]&lt;br /&gt;
             [prev-ctl 0.0]&lt;br /&gt;
             [prev-atl 0.0]&lt;br /&gt;
             [acc &#039;()])&lt;br /&gt;
    (if (&amp;gt; current end-secs)&lt;br /&gt;
        (reverse acc)&lt;br /&gt;
        (let* ([date-str (date-seconds-&amp;gt;string current)]&lt;br /&gt;
               [tss (hash-ref data-map date-str 0.0)]&lt;br /&gt;
               [current-ctl (calc-next-ewma tss prev-ctl ctl-alpha)]&lt;br /&gt;
               [current-atl (calc-next-ewma tss prev-atl atl-alpha)]&lt;br /&gt;
               [current-tsb (- current-ctl current-atl)])&lt;br /&gt;
          (loop (+ current SECONDS-PER-DAY)&lt;br /&gt;
                current-ctl&lt;br /&gt;
                current-atl&lt;br /&gt;
                (cons (list current date-str tss current-ctl current-atl current-tsb) acc))))))&lt;br /&gt;
&lt;br /&gt;
;; --- Plottning (Standardiserad och Stabil) ---&lt;br /&gt;
(define (plot-training-data calculated-timeline)&lt;br /&gt;
  (define-values (tss-pts ctl-pts atl-pts tsb-pts)&lt;br /&gt;
    (for/fold ([tss-acc &#039;()] [ctl-acc &#039;()] [atl-acc &#039;()] [tsb-acc &#039;()])&lt;br /&gt;
              ([day calculated-timeline])&lt;br /&gt;
      (match-let ([(list secs date tss ctl atl tsb) day])&lt;br /&gt;
        (values (cons (list secs tss) tss-acc)&lt;br /&gt;
                (cons (list secs ctl) ctl-acc)&lt;br /&gt;
                (cons (list secs atl) atl-acc)&lt;br /&gt;
                (cons (list secs tsb) tsb-acc)))))&lt;br /&gt;
&lt;br /&gt;
  ;; Använd enbart standardskalor och inbyggda date-ticks som bevisat fungerar&lt;br /&gt;
  (parameterize ([plot-x-ticks (date-ticks)]&lt;br /&gt;
                 [plot-x-label &amp;quot;Datum&amp;quot;]&lt;br /&gt;
                 [plot-y-label &amp;quot;Värde (TSS / CTL / ATL / TSB)&amp;quot;])&lt;br /&gt;
    &lt;br /&gt;
    (plot (list (lines (reverse tss-pts) #:color &amp;quot;lightgray&amp;quot; #:width 1 #:label &amp;quot;TSS&amp;quot;)&lt;br /&gt;
                (lines (reverse ctl-pts) #:color &amp;quot;blue&amp;quot; #:width 2 #:label &amp;quot;CTL (Kondition)&amp;quot;)&lt;br /&gt;
                (lines (reverse atl-pts) #:color &amp;quot;red&amp;quot; #:width 2 #:label &amp;quot;ATL (Trötthet)&amp;quot;)&lt;br /&gt;
                ;; TSB ritas med långa streck för att sticka ut tydligt på samma skala&lt;br /&gt;
                (lines (reverse tsb-pts) #:color &amp;quot;forestgreen&amp;quot; #:width 2 #:style &#039;long-dash #:label &amp;quot;TSB (Form)&amp;quot;))&lt;br /&gt;
          #:title &amp;quot;Träningsbelastning&amp;quot;&lt;br /&gt;
          #:legend-anchor &#039;top-left)))&lt;br /&gt;
&lt;br /&gt;
;; --- Huvudfunktion ---&lt;br /&gt;
(define (process-training-data input-file output-file)&lt;br /&gt;
  (define raw-lines (read-csv-file input-file))&lt;br /&gt;
  &lt;br /&gt;
  (define data-lines &lt;br /&gt;
    (filter (lambda (row) &lt;br /&gt;
              (and (not (null? row)) &lt;br /&gt;
                   (regexp-match? #px&amp;quot;[0-9]&amp;quot; (car row))&lt;br /&gt;
                   (not (string-prefix? (string-trim (car row)) &amp;quot;Datum&amp;quot;))))&lt;br /&gt;
            raw-lines))&lt;br /&gt;
  &lt;br /&gt;
  (define data-map&lt;br /&gt;
    (for/hash ([row data-lines])&lt;br /&gt;
      (define clean-date (date-seconds-&amp;gt;string (string-&amp;gt;date-seconds (car row))))&lt;br /&gt;
      (define raw-tss (string-&amp;gt;number (string-trim (cadr row) (regexp &amp;quot;[\&amp;quot;&#039;\r\n ]&amp;quot;))))&lt;br /&gt;
      (define clean-tss (if raw-tss raw-tss 0.0))&lt;br /&gt;
      (values clean-date clean-tss)))&lt;br /&gt;
  &lt;br /&gt;
  (define sorted-secs &lt;br /&gt;
    (sort (map string-&amp;gt;date-seconds (hash-keys data-map)) &amp;lt;))&lt;br /&gt;
  &lt;br /&gt;
  (when (null? sorted-secs)&lt;br /&gt;
    (error &amp;quot;Ingen giltig träningsdata hittades i filen.&amp;quot;))&lt;br /&gt;
  &lt;br /&gt;
  (define start-secs (car sorted-secs))&lt;br /&gt;
  (define end-secs (last sorted-secs))&lt;br /&gt;
  &lt;br /&gt;
  (define calculated-timeline &lt;br /&gt;
    (fill-and-compute-metrics data-map start-secs end-secs))&lt;br /&gt;
  &lt;br /&gt;
  (define header &#039;(&amp;quot;Datum&amp;quot; &amp;quot;TSS&amp;quot; &amp;quot;CTL&amp;quot; &amp;quot;ATL&amp;quot; &amp;quot;TSB&amp;quot;))&lt;br /&gt;
  (define output-rows&lt;br /&gt;
    (cons header&lt;br /&gt;
          (map (lambda (day-data)&lt;br /&gt;
                 (match-let ([(list secs date tss ctl atl tsb) day-data])&lt;br /&gt;
                   (list date&lt;br /&gt;
                         (number-&amp;gt;string (exact-&amp;gt;inexact tss))&lt;br /&gt;
                         (real-&amp;gt;decimal-string ctl 1)&lt;br /&gt;
                         (real-&amp;gt;decimal-string atl 1)&lt;br /&gt;
                         (real-&amp;gt;decimal-string tsb 1))))&lt;br /&gt;
               calculated-timeline)))&lt;br /&gt;
  &lt;br /&gt;
  (write-file output-file &lt;br /&gt;
              (string-join &lt;br /&gt;
               (map (lambda (row) (string-join row &amp;quot;,&amp;quot;)) output-rows)&lt;br /&gt;
               &amp;quot;\n&amp;quot;))&lt;br /&gt;
  (printf &amp;quot;Klart! Data har fyllts i dag för dag och skrivits till ~a\n&amp;quot; output-file)&lt;br /&gt;
  &lt;br /&gt;
  (plot-training-data calculated-timeline))&lt;br /&gt;
&lt;br /&gt;
;; --- KÖR PROGRAMMET ---&lt;br /&gt;
(process-training-data &amp;quot;training.csv&amp;quot; &amp;quot;training_calculated.csv&amp;quot;)&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/TSS&amp;diff=1937</id>
		<title>Racket/TSS</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/TSS&amp;diff=1937"/>
		<updated>2026-07-09T15:39:40Z</updated>

		<summary type="html">&lt;p&gt;Anders: Skapade sidan med &amp;#039;&amp;lt;pre&amp;gt; #lang racket  ;;;;;;;;;;; ;; Anders Sikvall Sommarhack 2026 ;; 2026-07-09 &amp;lt;anders@sikvall.se&amp;gt; ;; ;; Använder &amp;quot;TrainingPeaks&amp;quot; metod för beräkningar av de olika parametrarna ;; baserade på Training Stress Score (TSS) som i sin tur ger de olika ;; måtten enligt: ;; ;; TSS -- Training stress score, belastningsmått för motionsaktiviteter ;; CTL -- Chronic Training Level, TSS utslaget över 6 veckors tid, viktat mot närtid ;; ATL -- Acuste Training Level, TSS uts...&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;;;;;;;&lt;br /&gt;
;; Anders Sikvall Sommarhack 2026&lt;br /&gt;
;; 2026-07-09 &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
;;&lt;br /&gt;
;; Använder &amp;quot;TrainingPeaks&amp;quot; metod för beräkningar av de olika parametrarna&lt;br /&gt;
;; baserade på Training Stress Score (TSS) som i sin tur ger de olika&lt;br /&gt;
;; måtten enligt:&lt;br /&gt;
;;&lt;br /&gt;
;; TSS -- Training stress score, belastningsmått för motionsaktiviteter&lt;br /&gt;
;; CTL -- Chronic Training Level, TSS utslaget över 6 veckors tid, viktat mot närtid&lt;br /&gt;
;; ATL -- Acuste Training Level, TSS utslaget över senaste 7 dagarna&lt;br /&gt;
;; TSB -- Skillnaden mellan ATL-CTL, negativt betyder förbättring&lt;br /&gt;
&lt;br /&gt;
(require 2htdp/batch-io)&lt;br /&gt;
(require racket/date)&lt;br /&gt;
&lt;br /&gt;
;; --- Konstanter och Alfa-värden ---&lt;br /&gt;
(define ctl-alpha (/ 2 (+ 42 1)))&lt;br /&gt;
(define atl-alpha (/ 2 (+ 7 1)))&lt;br /&gt;
(define SECONDS-PER-DAY 86400)&lt;br /&gt;
&lt;br /&gt;
(define (calc-next-ewma current-tss previous-val alpha)&lt;br /&gt;
  (+ (* current-tss alpha) (* previous-val (- 1 alpha))))&lt;br /&gt;
&lt;br /&gt;
;; Datumhantering&lt;br /&gt;
(define (string-&amp;gt;date-seconds raw-str)&lt;br /&gt;
  ;; Rensa bort eventuella citattecken eller dolda tecken&lt;br /&gt;
  (define clean-str (string-trim raw-str (regexp &amp;quot;[\&amp;quot;&#039;\r\n ]&amp;quot;)))&lt;br /&gt;
  ;; Matcha och stycka upp strängen i YYYY MM DD.&lt;br /&gt;
  (match (regexp-match #px&amp;quot;([0-9]{4})-([0-9]{2})-([0-9]{2})&amp;quot; clean-str)&lt;br /&gt;
    [(list _ year-str month-str day-str)&lt;br /&gt;
     (find-seconds 0 0 12 &lt;br /&gt;
                   (string-&amp;gt;number day-str)&lt;br /&gt;
                   (string-&amp;gt;number month-str)&lt;br /&gt;
                   (string-&amp;gt;number year-str))]&lt;br /&gt;
    [_ (error &amp;quot;Felaktigt datumformat, använd YYYY-MM-DD:&amp;quot; raw-str)]))&lt;br /&gt;
&lt;br /&gt;
;; Gör om sekunder tillbaka till &amp;quot;YYYY-MM-DD&amp;quot;&lt;br /&gt;
(define (date-seconds-&amp;gt;string secs)&lt;br /&gt;
  (define d (seconds-&amp;gt;date secs))&lt;br /&gt;
  (format &amp;quot;~a-~a-~a&amp;quot;&lt;br /&gt;
          (date-year d)&lt;br /&gt;
          (if (&amp;lt; (date-month d) 10) (format &amp;quot;0~a&amp;quot; (date-month d)) (date-month d))&lt;br /&gt;
          (if (&amp;lt; (date-day d) 10) (format &amp;quot;0~a&amp;quot; (date-day d)) (date-day d))))&lt;br /&gt;
&lt;br /&gt;
;; --- Ackumulering och Utfyllnad ---&lt;br /&gt;
(define (fill-and-compute-metrics data-map start-secs end-secs)&lt;br /&gt;
  (let loop ([current start-secs]&lt;br /&gt;
             [prev-ctl 0.0]&lt;br /&gt;
             [prev-atl 0.0]&lt;br /&gt;
             [acc &#039;()])&lt;br /&gt;
    (if (&amp;gt; current end-secs)&lt;br /&gt;
        (reverse acc)&lt;br /&gt;
        (let* ([date-str (date-seconds-&amp;gt;string current)]&lt;br /&gt;
               [tss (hash-ref data-map date-str 0.0)]&lt;br /&gt;
               [current-ctl (calc-next-ewma tss prev-ctl ctl-alpha)]&lt;br /&gt;
               [current-atl (calc-next-ewma tss prev-atl atl-alpha)]&lt;br /&gt;
               [current-tsb (- current-ctl current-atl)])&lt;br /&gt;
          (loop (+ current SECONDS-PER-DAY)&lt;br /&gt;
                current-ctl&lt;br /&gt;
                current-atl&lt;br /&gt;
                (cons (list date-str tss current-ctl current-atl current-tsb) acc))))))&lt;br /&gt;
&lt;br /&gt;
;; --- Huvudfunktion ---&lt;br /&gt;
(define (process-training-data input-file output-file)&lt;br /&gt;
  (define raw-lines (read-csv-file input-file))&lt;br /&gt;
  &lt;br /&gt;
  ;; Filtrera bort rader som är tomma eller inte innehåller siffror i första kolumnen&lt;br /&gt;
  (define data-lines &lt;br /&gt;
    (filter (lambda (row) &lt;br /&gt;
              (and (not (null? row)) &lt;br /&gt;
                   (regexp-match? #px&amp;quot;[0-9]&amp;quot; (car row))&lt;br /&gt;
                   (not (string-prefix? (string-trim (car row)) &amp;quot;Datum&amp;quot;))))&lt;br /&gt;
            raw-lines))&lt;br /&gt;
  &lt;br /&gt;
  ;; Bygg en Hash-map för snabb uppslagning av Datum -&amp;gt; TSS&lt;br /&gt;
  (define data-map&lt;br /&gt;
    (for/hash ([row data-lines])&lt;br /&gt;
      (define clean-date (date-seconds-&amp;gt;string (string-&amp;gt;date-seconds (car row))))&lt;br /&gt;
      (define clean-tss (string-&amp;gt;number (string-trim (cadr row) (regexp &amp;quot;[\&amp;quot;&#039;\r\n ]&amp;quot;))))&lt;br /&gt;
      (values clean-date clean-tss)))&lt;br /&gt;
  &lt;br /&gt;
  ;; Sortera alla unika datum för att hitta start och slut&lt;br /&gt;
  (define sorted-secs &lt;br /&gt;
    (sort (map string-&amp;gt;date-seconds (hash-keys data-map)) &amp;lt;))&lt;br /&gt;
  &lt;br /&gt;
  (when (null? sorted-secs)&lt;br /&gt;
    (error &amp;quot;Ingen giltig träningsdata hittades i filen.&amp;quot;))&lt;br /&gt;
  &lt;br /&gt;
  (define start-secs (car sorted-secs))&lt;br /&gt;
  (define end-secs (last sorted-secs))&lt;br /&gt;
  &lt;br /&gt;
  ;; Beräkna kompletta metrics dag för dag&lt;br /&gt;
  (define calculated-timeline &lt;br /&gt;
    (fill-and-compute-metrics data-map start-secs end-secs))&lt;br /&gt;
  &lt;br /&gt;
  ;; Formatera för export&lt;br /&gt;
  (define header &#039;(&amp;quot;Datum&amp;quot; &amp;quot;TSS&amp;quot; &amp;quot;CTL&amp;quot; &amp;quot;ATL&amp;quot; &amp;quot;TSB&amp;quot;))&lt;br /&gt;
  (define output-rows&lt;br /&gt;
    (cons header&lt;br /&gt;
          (map (lambda (day-data)&lt;br /&gt;
                 (match-let ([(list date tss ctl atl tsb) day-data])&lt;br /&gt;
                   (list date&lt;br /&gt;
                         (number-&amp;gt;string (exact-&amp;gt;inexact tss))&lt;br /&gt;
                         (real-&amp;gt;decimal-string ctl 1)&lt;br /&gt;
                         (real-&amp;gt;decimal-string atl 1)&lt;br /&gt;
                         (real-&amp;gt;decimal-string tsb 1))))&lt;br /&gt;
               calculated-timeline)))&lt;br /&gt;
  &lt;br /&gt;
  ;; Skriv till CSV-fil&lt;br /&gt;
  (write-file output-file &lt;br /&gt;
              (string-join &lt;br /&gt;
               (map (lambda (row) (string-join row &amp;quot;,&amp;quot;)) output-rows)&lt;br /&gt;
               &amp;quot;\n&amp;quot;))&lt;br /&gt;
  (printf &amp;quot;Klart! Data har fyllts i dag för dag och skrivits till ~a\n&amp;quot; output-file))&lt;br /&gt;
&lt;br /&gt;
;; --- KÖR PROGRAMMET ---&lt;br /&gt;
(process-training-data &amp;quot;training.csv&amp;quot; &amp;quot;training_calculated.csv&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket&amp;diff=1936</id>
		<title>Racket</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket&amp;diff=1936"/>
		<updated>2026-07-09T15:30:23Z</updated>

		<summary type="html">&lt;p&gt;Anders: /* Andra kodexempel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Racket är ett programmeringsspråk i Lispfamiljen och underfamilj till Scheme. Det är kompatibelt med Scheme R5RS och andra varianter men har också ett eget väl utbyggt bibliotek av funktioner och rutiner och kanske en av de enklaste språken att komma igång och skapa GUI-applikationer i.&lt;br /&gt;
&lt;br /&gt;
= Installation Ubuntu = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo add-apt-repository ppa:plt/racket -y&lt;br /&gt;
sudo apt update &lt;br /&gt;
sudo apt install racket&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kör igång det inbyggda IDE:t med &amp;quot;drracket&amp;quot; antingen från applikationsmenyn eller terminalen.&lt;br /&gt;
&lt;br /&gt;
= Föreläsningspresentation Sommarhack 2026 =&lt;br /&gt;
&lt;br /&gt;
Presentation som PDF: &lt;br /&gt;
https://drive.google.com/file/d/1NK2Y--V2-ystgQS9sXWq9W_p5LChSvrq/view?usp=sharing&lt;br /&gt;
&lt;br /&gt;
= Kodexempel (Sommarhack 2026) =&lt;br /&gt;
&lt;br /&gt;
# [[Racket/Gissanumret]]&lt;br /&gt;
# [[Racket/Erastothenes]]&lt;br /&gt;
# [[Racket/Hexdump]]&lt;br /&gt;
# [[Racket/base24]]&lt;br /&gt;
# [[Racket/Personreg]]&lt;br /&gt;
# [[Racket/Fisher-Yates Shuffle]]&lt;br /&gt;
# [[Racket/RLE]]&lt;br /&gt;
# [[Racket/Lille-GUI]]&lt;br /&gt;
# [[Racket/Multitråd]]&lt;br /&gt;
# [[Racket/6502-emu]]&lt;br /&gt;
# [[Racket/Genetic]]&lt;br /&gt;
# [[Racket/Markov]]&lt;br /&gt;
# [[Racket/Neural]]&lt;br /&gt;
# [[Racket/Posits]]&lt;br /&gt;
# [[Racket/3D-boll]] -- från Olme&lt;br /&gt;
&lt;br /&gt;
= Andra kodexempel =&lt;br /&gt;
&lt;br /&gt;
# [[Racket/EFRW]]&lt;br /&gt;
# [[Racket/TSS]] Beräkna TSS, CTL, ATL och TSB från given datafil&lt;br /&gt;
&lt;br /&gt;
[[category:Racket]]&lt;br /&gt;
[[category:Programmering]]&lt;br /&gt;
[[category:Algoritmer]]&lt;br /&gt;
[[category:Förgreningssida]]&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket&amp;diff=1935</id>
		<title>Racket</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket&amp;diff=1935"/>
		<updated>2026-07-08T14:23:27Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Racket är ett programmeringsspråk i Lispfamiljen och underfamilj till Scheme. Det är kompatibelt med Scheme R5RS och andra varianter men har också ett eget väl utbyggt bibliotek av funktioner och rutiner och kanske en av de enklaste språken att komma igång och skapa GUI-applikationer i.&lt;br /&gt;
&lt;br /&gt;
= Installation Ubuntu = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo add-apt-repository ppa:plt/racket -y&lt;br /&gt;
sudo apt update &lt;br /&gt;
sudo apt install racket&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kör igång det inbyggda IDE:t med &amp;quot;drracket&amp;quot; antingen från applikationsmenyn eller terminalen.&lt;br /&gt;
&lt;br /&gt;
= Föreläsningspresentation Sommarhack 2026 =&lt;br /&gt;
&lt;br /&gt;
Presentation som PDF: &lt;br /&gt;
https://drive.google.com/file/d/1NK2Y--V2-ystgQS9sXWq9W_p5LChSvrq/view?usp=sharing&lt;br /&gt;
&lt;br /&gt;
= Kodexempel (Sommarhack 2026) =&lt;br /&gt;
&lt;br /&gt;
# [[Racket/Gissanumret]]&lt;br /&gt;
# [[Racket/Erastothenes]]&lt;br /&gt;
# [[Racket/Hexdump]]&lt;br /&gt;
# [[Racket/base24]]&lt;br /&gt;
# [[Racket/Personreg]]&lt;br /&gt;
# [[Racket/Fisher-Yates Shuffle]]&lt;br /&gt;
# [[Racket/RLE]]&lt;br /&gt;
# [[Racket/Lille-GUI]]&lt;br /&gt;
# [[Racket/Multitråd]]&lt;br /&gt;
# [[Racket/6502-emu]]&lt;br /&gt;
# [[Racket/Genetic]]&lt;br /&gt;
# [[Racket/Markov]]&lt;br /&gt;
# [[Racket/Neural]]&lt;br /&gt;
# [[Racket/Posits]]&lt;br /&gt;
# [[Racket/3D-boll]] -- från Olme&lt;br /&gt;
&lt;br /&gt;
= Andra kodexempel =&lt;br /&gt;
&lt;br /&gt;
# [[Racket/EFRW]]&lt;br /&gt;
&lt;br /&gt;
[[category:Racket]]&lt;br /&gt;
[[category:Programmering]]&lt;br /&gt;
[[category:Algoritmer]]&lt;br /&gt;
[[category:Förgreningssida]]&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Gissanumret&amp;diff=1934</id>
		<title>Racket/Gissanumret</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Gissanumret&amp;diff=1934"/>
		<updated>2026-07-08T13:31:24Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;#lang racket&lt;br /&gt;
&lt;br /&gt;
(define (nummerfråga tal)&lt;br /&gt;
  (display &amp;quot;Din gissning? &amp;quot;)&lt;br /&gt;
  (define guess (string-&amp;gt;number (read-line)))&lt;br /&gt;
  (cond [(&amp;gt; tal guess) (displayln &amp;quot;För lågt!&amp;quot;) (nummerfråga tal)]&lt;br /&gt;
        [(&amp;lt; tal guess) (displayln &amp;quot;För högt!&amp;quot;) (nummerfråga tal)]&lt;br /&gt;
        [else (displayln &amp;quot;Korrekt!&amp;quot;)]))&lt;br /&gt;
&lt;br /&gt;
(displayln &amp;quot;Gissa ett tal från 1 till och med 100.&amp;quot;)&lt;br /&gt;
(nummerfråga (random 1 100))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Gissanumret&amp;diff=1933</id>
		<title>Racket/Gissanumret</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Gissanumret&amp;diff=1933"/>
		<updated>2026-07-07T20:53:38Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;#lang racket&lt;br /&gt;
&lt;br /&gt;
(define (nummerfråga tal)&lt;br /&gt;
  (display &amp;quot;Din gissning? &amp;quot;)&lt;br /&gt;
  (define guess (string-&amp;gt;number (read-line)))&lt;br /&gt;
  (cond [(&amp;gt; tal guess) (displayln &amp;quot;För lågt!&amp;quot;) (nummerfråga tal)]&lt;br /&gt;
        [(&amp;lt; tal guess) (displayln &amp;quot;För högt!&amp;quot;) (nummerfråga tal)]&lt;br /&gt;
        [else (displayln &amp;quot;Korrekt!&amp;quot;)]))&lt;br /&gt;
&lt;br /&gt;
(displayln &amp;quot;Gissa ett tal från 1 till och med 100.&amp;quot;)&lt;br /&gt;
(nummerfråga (random 1 101))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Gissanumret&amp;diff=1932</id>
		<title>Racket/Gissanumret</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Gissanumret&amp;diff=1932"/>
		<updated>2026-07-07T20:48:00Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
(define (inquire-user number)&lt;br /&gt;
  (display &amp;quot;Din gissning? &amp;quot;)&lt;br /&gt;
  (define guess (string-&amp;gt;number (read-line)))&lt;br /&gt;
  (cond [(&amp;gt; number guess) (displayln &amp;quot;För lågt!&amp;quot;) (inquire-user number)]&lt;br /&gt;
        [(&amp;lt; number guess) (displayln &amp;quot;För högt!&amp;quot;) (inquire-user number)]&lt;br /&gt;
        [else (displayln &amp;quot;Korrekt!&amp;quot;)]))&lt;br /&gt;
&lt;br /&gt;
(displayln &amp;quot;Gissa ett tal från 1 till och med 100.&amp;quot;)&lt;br /&gt;
(inquire-user (random 1 101))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Gissanumret&amp;diff=1931</id>
		<title>Racket/Gissanumret</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Gissanumret&amp;diff=1931"/>
		<updated>2026-07-07T20:44:39Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
(define (inquire-user number)&lt;br /&gt;
  (display &amp;quot;Din gissning? &amp;quot;)&lt;br /&gt;
  (define guess (string-&amp;gt;number (read-line)))&lt;br /&gt;
  (cond [(&amp;gt; number guess) (displayln &amp;quot;Too low&amp;quot;) (inquire-user number)]&lt;br /&gt;
        [(&amp;lt; number guess) (displayln &amp;quot;Too high&amp;quot;) (inquire-user number)]&lt;br /&gt;
        [else (displayln &amp;quot;Correct!&amp;quot;)]))&lt;br /&gt;
&lt;br /&gt;
(displayln &amp;quot;Gissa ett tal från 1 till och med 100.&amp;quot;)&lt;br /&gt;
(inquire-user (random 1 101))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Gissanumret&amp;diff=1930</id>
		<title>Racket/Gissanumret</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Gissanumret&amp;diff=1930"/>
		<updated>2026-07-07T20:43:53Z</updated>

		<summary type="html">&lt;p&gt;Anders: Skapade sidan med &amp;#039;&amp;lt;pre&amp;gt;  #lang racket  (define (inquire-user number)   (display &amp;quot;Insert a number: &amp;quot;)   (define guess (string-&amp;gt;number (read-line)))   (cond [(&amp;gt; number guess) (displayln &amp;quot;Too low&amp;quot;) (inquire-user number)]         [(&amp;lt; number guess) (displayln &amp;quot;Too high&amp;quot;) (inquire-user number)]         [else (displayln &amp;quot;Correct!&amp;quot;)]))  (displayln &amp;quot;Guess a number between 1 and 100&amp;quot;) (inquire-user (random 1 101))    &amp;lt;/pre&amp;gt;&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
(define (inquire-user number)&lt;br /&gt;
  (display &amp;quot;Insert a number: &amp;quot;)&lt;br /&gt;
  (define guess (string-&amp;gt;number (read-line)))&lt;br /&gt;
  (cond [(&amp;gt; number guess) (displayln &amp;quot;Too low&amp;quot;) (inquire-user number)]&lt;br /&gt;
        [(&amp;lt; number guess) (displayln &amp;quot;Too high&amp;quot;) (inquire-user number)]&lt;br /&gt;
        [else (displayln &amp;quot;Correct!&amp;quot;)]))&lt;br /&gt;
&lt;br /&gt;
(displayln &amp;quot;Guess a number between 1 and 100&amp;quot;)&lt;br /&gt;
(inquire-user (random 1 101))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket&amp;diff=1929</id>
		<title>Racket</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket&amp;diff=1929"/>
		<updated>2026-07-07T20:43:41Z</updated>

		<summary type="html">&lt;p&gt;Anders: /* Kodexempel (Sommarhack 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Racket är ett programmeringsspråk i Lispfamiljen och underfamilj till Scheme. Det är kompatibelt med Scheme R5RS och andra varianter men har också ett eget väl utbyggt bibliotek av funktioner och rutiner och kanske en av de enklaste språken att komma igång och skapa GUI-applikationer i.&lt;br /&gt;
&lt;br /&gt;
= Installation Ubuntu = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo add-apt-repository ppa:plt/racket -y&lt;br /&gt;
sudo apt update &lt;br /&gt;
sudo apt install racket&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kör igång det inbyggda IDE:t med &amp;quot;drracket&amp;quot; antingen från applikationsmenyn eller terminalen.&lt;br /&gt;
&lt;br /&gt;
= Kodexempel (Sommarhack 2026) =&lt;br /&gt;
&lt;br /&gt;
# [[Racket/Gissanumret]]&lt;br /&gt;
# [[Racket/Erastothenes]]&lt;br /&gt;
# [[Racket/Hexdump]]&lt;br /&gt;
# [[Racket/base24]]&lt;br /&gt;
# [[Racket/Personreg]]&lt;br /&gt;
# [[Racket/Fisher-Yates Shuffle]]&lt;br /&gt;
# [[Racket/RLE]]&lt;br /&gt;
# [[Racket/Lille-GUI]]&lt;br /&gt;
# [[Racket/Multitråd]]&lt;br /&gt;
# [[Racket/6502-emu]]&lt;br /&gt;
# [[Racket/Genetic]]&lt;br /&gt;
# [[Racket/Markov]]&lt;br /&gt;
# [[Racket/Neural]]&lt;br /&gt;
# [[Racket/Posits]]&lt;br /&gt;
# [[Racket/3D-boll]] -- från Olme&lt;br /&gt;
&lt;br /&gt;
= Andra kodexempel =&lt;br /&gt;
&lt;br /&gt;
# [[Racket/EFRW]]&lt;br /&gt;
&lt;br /&gt;
[[category:Racket]]&lt;br /&gt;
[[category:Programmering]]&lt;br /&gt;
[[category:Algoritmer]]&lt;br /&gt;
[[category:Förgreningssida]]&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1928</id>
		<title>Racket/Erastothenes</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1928"/>
		<updated>2026-07-07T13:10:23Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;&lt;br /&gt;
;; Eratosthenes såll implementerad i Racket&lt;br /&gt;
;; Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se&lt;br /&gt;
;;;;;&lt;br /&gt;
&lt;br /&gt;
(require racket/format)&lt;br /&gt;
&lt;br /&gt;
;; Skapar en vektor där indexet motsvarar talet.&lt;br /&gt;
;; Om (vector-ref v n) är #t, så är n ett primtal.&lt;br /&gt;
(define (make-sieve limit)&lt;br /&gt;
  ;; 1 SKAPA VEKTORN&lt;br /&gt;
  ;; Vi skapar en vektor med storleken (limit + 1). Eftersom index börjar på 0,&lt;br /&gt;
  ;; måste vektorn vara ett element större än &#039;limit&#039; för att &#039;limit&#039; ska få plats som sista index.&lt;br /&gt;
  ;; Vi antar från början att ALLA tal är primtal (#t), och sållar sedan bort de som inte är det.&lt;br /&gt;
  (define sieve (make-vector (add1 limit) #t))&lt;br /&gt;
  &lt;br /&gt;
  ;; 2 BASFALL (0 OCH 1)&lt;br /&gt;
  ;; 0 och 1 är aldrig primtal enligt definitionen (ett primtal måste vara större än 1).&lt;br /&gt;
  ;; Om användaren har angett en limit som inkluderar dessa, markerar vi dem direkt som #f (falskt).&lt;br /&gt;
  (when (&amp;gt;= limit 0) (vector-set! sieve 0 #f))&lt;br /&gt;
  (when (&amp;gt;= limit 1) (vector-set! sieve 1 #f))&lt;br /&gt;
  &lt;br /&gt;
  ;; 3 HUVUDLOOPEN (YTTRE)&lt;br /&gt;
  ;; Vi behöver bara gå upp till kvadratroten av limit (avrundat nedåt till heltal).&lt;br /&gt;
  ;; Varför? Om ett tal har en faktor som är större än kvadratroten, måste dess &lt;br /&gt;
  ;; motsvarande partnerfaktor vara mindre än kvadratroten. Har vi redan rensat&lt;br /&gt;
  ;; alla mindre faktorer så har vi automatiskt tagit hand om de större!&lt;br /&gt;
  (for ((p (in-range 2 (add1 (integer-sqrt limit)))))&lt;br /&gt;
    &lt;br /&gt;
    ;; Om index &#039;p&#039; fortfarande är #t, så har vi hittat ett primtal.&lt;br /&gt;
    (when (vector-ref sieve p)&lt;br /&gt;
      &lt;br /&gt;
      ;; 4 SÅLLNINGSLOOPEN (INRE)&lt;br /&gt;
      ;; Markera alla multiplar av p som falska (inte primtal).&lt;br /&gt;
      ;; Optimering: Vi startar direkt vid (* p p) istället för (* p 2).&lt;br /&gt;
      ;; Varför? Alla multiplar mindre än p*p (som 2p, 3p, 4p... upp till (p-1)p) &lt;br /&gt;
      ;; har redan markerats som #f i tidigare varv av den yttre loopen.&lt;br /&gt;
      ;; Det tredje argumentet &#039;p&#039; gör att loopen hoppar med p-steg i taget (p*p, p*p+p, p*p+2p, osv).&lt;br /&gt;
      (for ((multiple (in-range (* p p) (add1 limit) p)))&lt;br /&gt;
        (vector-set! sieve multiple #f)))) ; inte primtal&lt;br /&gt;
        &lt;br /&gt;
  ;; Returnera det färdiga sållet (vektorn) till anroparen&lt;br /&gt;
  sieve)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Returnerar en lista med alla primtal mellan start och stop&lt;br /&gt;
(define (primes-between start stop)&lt;br /&gt;
  ;; Kontrollera om intervallet är logiskt. Om start är större än stop finns det inga tal.&lt;br /&gt;
  (if (&amp;gt; start stop)&lt;br /&gt;
      &#039;() ; Returnerar tomt om start &amp;gt; stop&lt;br /&gt;
      &lt;br /&gt;
      ;; Skapa sållet för hela intervallet upp till &#039;stop&#039;.&lt;br /&gt;
      (let ((is-prime? (make-sieve stop)))&lt;br /&gt;
        &lt;br /&gt;
        ;; for/list samlar automatiskt ihop alla &#039;n&#039; som matchar villkoret till en lista.&lt;br /&gt;
        ;; (max start 2) förhindrar att vi letar efter primtal under 2, då de inte finns.&lt;br /&gt;
        (for/list ((n (in-range (max start 2) (add1 stop)))&lt;br /&gt;
                   #:when (vector-ref is-prime? n)) ; Villkor: Endast om index n är #t i sållet&lt;br /&gt;
          n))))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Hjälpmakro för att tajma hur lång tid operationen tog&lt;br /&gt;
(define-syntax-rule (timeit expr)&lt;br /&gt;
  ;; Spara starttiden i millisekunder innan uttrycket körs&lt;br /&gt;
  (let ((start (current-inexact-milliseconds)))&lt;br /&gt;
    ;; Kör uttrycket &#039;expr&#039; (t.ex. vår funktion) och spara resultatet i &#039;result&#039;&lt;br /&gt;
    (let ((result expr))&lt;br /&gt;
      ;; Skriv ut tidsskillnaden med snygg lokal formatering&lt;br /&gt;
      (printf &amp;quot;Tid: ~a ms\n&amp;quot; (~a (- (current-inexact-milliseconds) start)))&lt;br /&gt;
      result)))  ; Returnera svaret från expr så att programmet kan använda det&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Testkörning&lt;br /&gt;
(timeit (primes-between 10000000 10000500))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1927</id>
		<title>DBµV/m</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1927"/>
		<updated>2026-07-03T07:32:51Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Ofärdig}}&lt;br /&gt;
&lt;br /&gt;
Konvertera dBµV/m till dBm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; P_{\text{dBm}} = E_{\text{dB}\mu\text{V/m}} - 20\log(f) + G_{\text{dBi}} - 77,2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Där &lt;br /&gt;
&amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; anges i MHz och&lt;br /&gt;
&amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; är antennvinst jämfört med en isotrop antenn dBi.&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1926</id>
		<title>DBµV/m</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1926"/>
		<updated>2026-07-03T07:29:40Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Ofärdig}}&lt;br /&gt;
&lt;br /&gt;
Konvertera dBµV/m till dBm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; P_{dBm} = E_{dB\mu V/m} - 20\log(f) + G_{dBi} - 77,2 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Där &lt;br /&gt;
&amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; anges i MHz och&lt;br /&gt;
&amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; är antennvinst jämfört med en isotrop antenn dBi&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1925</id>
		<title>DBµV/m</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1925"/>
		<updated>2026-07-03T07:28:11Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Ofärdig}}&lt;br /&gt;
&lt;br /&gt;
Konvertera dBµV/m till dBm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; P_{dBm} = E_{dB\mu V/m} - 20\log(f) - 77,2 + G&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Där &lt;br /&gt;
&amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; anges i MHz och&lt;br /&gt;
&amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; är antennvinst jämfört med en isotrop antenn dBi&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1924</id>
		<title>DBµV/m</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1924"/>
		<updated>2026-07-03T07:27:32Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Ofärdig}}&lt;br /&gt;
&lt;br /&gt;
Konvertera dBµV/m till dBm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; P_{dBm} = E_{dB\mu V/m} - 20\log(f) - 77,2 + G&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Där &lt;br /&gt;
&amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; anges i MHz och&lt;br /&gt;
&amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; är antennvinst minus kabelförluster i dB&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1923</id>
		<title>DBµV/m</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1923"/>
		<updated>2026-07-03T07:26:38Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Ofärdig}}&lt;br /&gt;
&lt;br /&gt;
Konvertera dBµV/m till dBm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; P_{dBm} = E_{dB\mu V/m} - 20\log(f) - 77,2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Där &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; anges i MHz.&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1922</id>
		<title>DBµV/m</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1922"/>
		<updated>2026-07-03T07:25:23Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Ofärdig}}&lt;br /&gt;
&lt;br /&gt;
Konvertera dBµV/m till dBm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; P_{dBm} = E_{dB\mu V/m} - 20\log(f) - 77,2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detta gäller då för ett avstånd av 3 meter från sändarens antenn och inkluderar inte antenngain och andra parametrar, detta får läggas till eller dras från efter konverteringen.&lt;br /&gt;
&lt;br /&gt;
(I framtiden ska jag beskriva mer vad faktorn -95.2 kommer från)&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1921</id>
		<title>DBµV/m</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=DB%C2%B5V/m&amp;diff=1921"/>
		<updated>2026-07-03T07:25:00Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Ofärdig}}&lt;br /&gt;
&lt;br /&gt;
Konvertera dBµV/m till dBm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; P_{dBm} = E_{dB\mu /m} - 20\log(f) - 77,2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Detta gäller då för ett avstånd av 3 meter från sändarens antenn och inkluderar inte antenngain och andra parametrar, detta får läggas till eller dras från efter konverteringen.&lt;br /&gt;
&lt;br /&gt;
(I framtiden ska jag beskriva mer vad faktorn -95.2 kommer från)&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Neural&amp;diff=1920</id>
		<title>Racket/Neural</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Neural&amp;diff=1920"/>
		<updated>2026-07-02T14:42:43Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Algoritmer]]&lt;br /&gt;
[[category:Programmering]]&lt;br /&gt;
[[category:Racket]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;&lt;br /&gt;
;; Neuralt nät implementerad i Racket&lt;br /&gt;
;; Täpp-Anders Sikvall 2026-07-02 &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
;;;;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;;;;; NEURALT NÄT&lt;br /&gt;
;;;&lt;br /&gt;
;; Detta program implementerar ett litet neuralt nät och tränar det på sannings-&lt;br /&gt;
;; tabellen för XOR. Efter några tusen epoker ska prediktionerna ligga extremt nära&lt;br /&gt;
;; följande:&lt;br /&gt;
;;&lt;br /&gt;
;; a,b → a XOR b&lt;br /&gt;
;; 0,0 →    0&lt;br /&gt;
;; 0,1 →    1&lt;br /&gt;
;; 1,0 →    1&lt;br /&gt;
;; 1,1 →    0&lt;br /&gt;
;;&lt;br /&gt;
;; Implementationen har två input-neuroner för a och b&lt;br /&gt;
;; det finns 5 interna (dolda neuroner) som tränas av indatat&lt;br /&gt;
;; det finns 1 output-neuron som ger svaret&lt;br /&gt;
;;&lt;br /&gt;
;;;;; Kodens uppbyggnad och de olika funktionernas användning&lt;br /&gt;
;;&lt;br /&gt;
;; sigmoid, sigmoid-deriv      Sigmoid-kurva och derivata för backpropagation&lt;br /&gt;
;;&lt;br /&gt;
;; random-matrix, met-vec-mul  Grundläggande linalg&lt;br /&gt;
;; outer-product; vec-add&lt;br /&gt;
;;&lt;br /&gt;
;; struct nn                   Nätverksstruktur, lagrar vikter och bias&lt;br /&gt;
;;&lt;br /&gt;
;; forward                     Beräknar prediktion&lt;br /&gt;
;;&lt;br /&gt;
;; train-step                  En iteration av backpropagation&lt;br /&gt;
;;&lt;br /&gt;
;; train                       Kör många epoker av in och förväntat utdata för&lt;br /&gt;
;;                             att träna modellen&lt;br /&gt;
;;&lt;br /&gt;
;; predict                     Använder nätverket på ny data!&lt;br /&gt;
;;&lt;br /&gt;
;;&lt;br /&gt;
;;;;; Viktiga delar&lt;br /&gt;
;;&lt;br /&gt;
;; * sigmoid mappar alla värden steglöst till intervallet (0,1)&lt;br /&gt;
;; * derivatan används i backpropagation för att räkna ut gradienten&lt;br /&gt;
;;&lt;br /&gt;
;; w1: vikter mellan input och hidden layer (5x2 matris)&lt;br /&gt;
;; b1: bias för hidden layer (5-vektor)&lt;br /&gt;
;; w2: viktar mellan hidden och output (1x5 matris)&lt;br /&gt;
;; b2: bias för output (1-vektor)&lt;br /&gt;
;;&lt;br /&gt;
;; Forward pass&lt;br /&gt;
;;&lt;br /&gt;
;; 1. Beräkna aktivering i hidden layer&lt;br /&gt;
;;    z1 = W1 * input + b1&lt;br /&gt;
;;    h = rho z1&lt;br /&gt;
;; 2. Beräkna output&lt;br /&gt;
;;    z2 = W2 * h +b2&lt;br /&gt;
;;    output = rho(z2)&lt;br /&gt;
;;&lt;br /&gt;
;; Backpropagation (uppdatering av viktningen)&lt;br /&gt;
;;&lt;br /&gt;
;; Hjärtat i träningen av ett neuralt nät. Algorimen är &amp;quot;gradient descent&amp;quot; med batch-&lt;br /&gt;
;; storlek 1.&lt;br /&gt;
;;&lt;br /&gt;
;; 1. Forward pass hidden → output&lt;br /&gt;
;; 2. Output-felet&lt;br /&gt;
;;    delta = (output - target) X rho&#039; (output)&lt;br /&gt;
;; 3. Hidden-felet&lt;br /&gt;
;;    delta hidden = (W2/T * delta out) X rho&#039; (hidden)&lt;br /&gt;
;; 4. Uppdatera vikternas gradienter&lt;br /&gt;
;;    W2 ← W2 - nabla * delta out * h^T&lt;br /&gt;
;;    W1 ← W1 - nabla * delta hidden * input^T&lt;br /&gt;
;; 5. Bias uppdateras på samma sätt (jo det är en genväg)&lt;br /&gt;
;;&lt;br /&gt;
&lt;br /&gt;
(require math/base)&lt;br /&gt;
&lt;br /&gt;
;; ========================================&lt;br /&gt;
;; Aktiveringsfunktioner&lt;br /&gt;
;; ========================================&lt;br /&gt;
(define (sigmoid x)&lt;br /&gt;
  (/ 1.0 (+ 1.0 (exp (- x)))))&lt;br /&gt;
&lt;br /&gt;
(define (sigmoid-deriv y)   ; y måste redan vara sigmoid(x)&lt;br /&gt;
  (* y (- 1.0 y)))&lt;br /&gt;
&lt;br /&gt;
;; ========================================&lt;br /&gt;
;; Matris-/vektorhjälp (enkel version)&lt;br /&gt;
;; ========================================&lt;br /&gt;
(define (random-matrix rows cols)&lt;br /&gt;
  (build-vector rows&lt;br /&gt;
                (λ (_) (build-vector cols (λ (_) (- (* 2.0 (random)) 1.0))))))&lt;br /&gt;
&lt;br /&gt;
(define (vec-add v1 v2)     ; elementvis addition av två vektorer&lt;br /&gt;
  (build-vector (vector-length v1)&lt;br /&gt;
                (λ (i) (+ (vector-ref v1 i) (vector-ref v2 i)))))&lt;br /&gt;
&lt;br /&gt;
(define (scalar-mul s v)&lt;br /&gt;
  (build-vector (vector-length v) (λ (i) (* s (vector-ref v i)))))&lt;br /&gt;
&lt;br /&gt;
(define (outer-product v1 v2)   ; v1 kolumn × v2 rad → matris&lt;br /&gt;
  (build-vector (vector-length v1)&lt;br /&gt;
                (λ (i)&lt;br /&gt;
                  (build-vector (vector-length v2)&lt;br /&gt;
                                (λ (j) (* (vector-ref v1 i) (vector-ref v2 j)))))))&lt;br /&gt;
&lt;br /&gt;
(define (mat-vec-mul M v)       ; M (rows×cols) × v (cols)&lt;br /&gt;
  (build-vector (vector-length M)&lt;br /&gt;
                (λ (i)&lt;br /&gt;
                  (for/sum ([j (in-range (vector-length v))])&lt;br /&gt;
                    (* (vector-ref (vector-ref M i) j)&lt;br /&gt;
                       (vector-ref v j))))))&lt;br /&gt;
&lt;br /&gt;
(define (transpose M)&lt;br /&gt;
  (build-vector (vector-length (vector-ref M 0))&lt;br /&gt;
                (λ (j)&lt;br /&gt;
                  (build-vector (vector-length M)&lt;br /&gt;
                                (λ (i) (vector-ref (vector-ref M i) j))))))&lt;br /&gt;
&lt;br /&gt;
;; ======================================== &lt;br /&gt;
;; Neuralt nätverk&lt;br /&gt;
;; ========================================&lt;br /&gt;
(struct nn (w1 b1 w2 b2) #:transparent)&lt;br /&gt;
&lt;br /&gt;
(define (make-nn in-size hidden-size out-size)&lt;br /&gt;
  (nn (random-matrix hidden-size in-size)&lt;br /&gt;
      (make-vector hidden-size 0.0)&lt;br /&gt;
      (random-matrix out-size hidden-size)&lt;br /&gt;
      (make-vector out-size 0.0)))&lt;br /&gt;
&lt;br /&gt;
(define (forward net input)          ; input är en vektor t.ex. #(0 0)&lt;br /&gt;
  (let* ([z1 (vec-add (mat-vec-mul (nn-w1 net) input)&lt;br /&gt;
                      (nn-b1 net))]&lt;br /&gt;
         [h  (build-vector (vector-length z1) (λ (i) (sigmoid (vector-ref z1 i))))]&lt;br /&gt;
         [z2 (vec-add (mat-vec-mul (nn-w2 net) h)&lt;br /&gt;
                      (nn-b2 net))]&lt;br /&gt;
         [o  (build-vector (vector-length z2) (λ (i) (sigmoid (vector-ref z2 i))))])&lt;br /&gt;
    (values o h)))&lt;br /&gt;
&lt;br /&gt;
(define (train-step net input target lr)&lt;br /&gt;
  (let-values ([(output hidden) (forward net input)])&lt;br /&gt;
    (let* ([out-err (build-vector (vector-length output)&lt;br /&gt;
                                  (λ (i) (- (vector-ref output i)&lt;br /&gt;
                                            (vector-ref target i))))]&lt;br /&gt;
           [out-delta (build-vector (vector-length output)&lt;br /&gt;
                                    (λ (i) (* (vector-ref out-err i)&lt;br /&gt;
                                              (sigmoid-deriv (vector-ref output i)))))]&lt;br /&gt;
&lt;br /&gt;
           [hidden-err (mat-vec-mul (transpose (nn-w2 net)) out-delta)]&lt;br /&gt;
           [hidden-delta (build-vector (vector-length hidden)&lt;br /&gt;
                                       (λ (i) (* (vector-ref hidden-err i)&lt;br /&gt;
                                                 (sigmoid-deriv (vector-ref hidden i)))))]&lt;br /&gt;
&lt;br /&gt;
           ;; Uppdateringar&lt;br /&gt;
           [dw2 (outer-product out-delta hidden)]&lt;br /&gt;
           [new-w2 (build-vector (vector-length (nn-w2 net))&lt;br /&gt;
                                 (λ (i)&lt;br /&gt;
                                   (build-vector (vector-length (vector-ref (nn-w2 net) i))&lt;br /&gt;
                                                 (λ (j) (- (vector-ref (vector-ref (nn-w2 net) i) j)&lt;br /&gt;
                                                           (* lr (vector-ref (vector-ref dw2 i) j)))))))]&lt;br /&gt;
           [new-b2 (build-vector (vector-length (nn-b2 net))&lt;br /&gt;
                                 (λ (i) (- (vector-ref (nn-b2 net) i)&lt;br /&gt;
                                           (* lr (vector-ref out-delta i)))))]&lt;br /&gt;
&lt;br /&gt;
           [dw1 (outer-product hidden-delta input)]&lt;br /&gt;
           [new-w1 (build-vector (vector-length (nn-w1 net))&lt;br /&gt;
                                 (λ (i)&lt;br /&gt;
                                   (build-vector (vector-length (vector-ref (nn-w1 net) i))&lt;br /&gt;
                                                 (λ (j) (- (vector-ref (vector-ref (nn-w1 net) i) j)&lt;br /&gt;
                                                           (* lr (vector-ref (vector-ref dw1 i) j)))))))]&lt;br /&gt;
           [new-b1 (build-vector (vector-length (nn-b1 net))&lt;br /&gt;
                                 (λ (i) (- (vector-ref (nn-b1 net) i)&lt;br /&gt;
                                           (* lr (vector-ref hidden-delta i)))))])&lt;br /&gt;
&lt;br /&gt;
      (nn new-w1 new-b1 new-w2 new-b2))))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; ========================================&lt;br /&gt;
;; Skriv ut vikter och bias – snygg tabell (fixad version)&lt;br /&gt;
;; ========================================&lt;br /&gt;
(define (print-nn net)&lt;br /&gt;
  (printf &amp;quot;\n=== NEURALT NÄTVERK - VIKTER OCH BIAS ===\n\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  ;; Hjälpfunktion för att formatera tal snyggt (med minustecken på rätt plats)&lt;br /&gt;
  (define (fmt x)&lt;br /&gt;
    (~r x &lt;br /&gt;
        #:min-width 10 &lt;br /&gt;
        #:precision &#039;(= 4)&lt;br /&gt;
        #:sign #f))          ; #f = visa minus för negativa, inget tecken för positiva&lt;br /&gt;
&lt;br /&gt;
  ;; W1: Vikter från input till hidden (5×2)&lt;br /&gt;
  (printf &amp;quot;W1 (Hidden × Input):\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;          Input0      Input1\n&amp;quot;)&lt;br /&gt;
  (for ([i (in-range (vector-length (nn-w1 net)))])&lt;br /&gt;
    (printf &amp;quot;Hidden~a  &amp;quot; i)&lt;br /&gt;
    (for ([j (in-range 2)])&lt;br /&gt;
      (printf &amp;quot;~a  &amp;quot; (fmt (vector-ref (vector-ref (nn-w1 net) i) j))))&lt;br /&gt;
    (printf &amp;quot;\n&amp;quot;))&lt;br /&gt;
  (printf &amp;quot;\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  ;; b1: Bias för hidden layer&lt;br /&gt;
  (printf &amp;quot;b1 (Bias hidden layer):\n&amp;quot;)&lt;br /&gt;
  (for ([i (in-range (vector-length (nn-b1 net)))])&lt;br /&gt;
    (printf &amp;quot;~a  &amp;quot; (fmt (vector-ref (nn-b1 net) i))))&lt;br /&gt;
  (printf &amp;quot;\n\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  ;; W2: Vikter från hidden till output (1×5)&lt;br /&gt;
  (printf &amp;quot;W2 (Output × Hidden):\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;         Hidden0     Hidden1     Hidden2     Hidden3     Hidden4\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;Output   &amp;quot;)&lt;br /&gt;
  (for ([j (in-range 5)])&lt;br /&gt;
    (printf &amp;quot;~a  &amp;quot; (fmt (vector-ref (vector-ref (nn-w2 net) 0) j))))&lt;br /&gt;
  (printf &amp;quot;\n\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  ;; b2: Bias för output&lt;br /&gt;
  (printf &amp;quot;b2 (Bias output):\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;~a\n&amp;quot; (fmt (vector-ref (nn-b2 net) 0)))&lt;br /&gt;
&lt;br /&gt;
  (printf &amp;quot;\n========================================\n&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Träna nätverket&lt;br /&gt;
(define (train net data epochs lr)&lt;br /&gt;
  (for ([ep (in-range epochs)])&lt;br /&gt;
    (for ([sample data])&lt;br /&gt;
      (set! net (train-step net (car sample) (cadr sample) lr)))&lt;br /&gt;
    &lt;br /&gt;
    (when (zero? (modulo ep 500))      ; oftare än var 1000:e&lt;br /&gt;
      (let ([loss (for/sum ([sample data])&lt;br /&gt;
                    (let-values ([(output _) (forward net (car sample))])&lt;br /&gt;
                      (let ([target (vector-ref (cadr sample) 0)]&lt;br /&gt;
                            [pred   (vector-ref output 0)])&lt;br /&gt;
                        (sqr (- target pred)))))])&lt;br /&gt;
        (printf &amp;quot;Epoch ~a  Loss: ~a\n&amp;quot; ep (/ loss (length data))))))&lt;br /&gt;
  (print-nn net)&lt;br /&gt;
  net)&lt;br /&gt;
&lt;br /&gt;
;; Prediktera&lt;br /&gt;
(define (predict net input)&lt;br /&gt;
  (let-values ([(output _) (forward net input)])&lt;br /&gt;
    output))&lt;br /&gt;
&lt;br /&gt;
;; ========================================&lt;br /&gt;
;; XOR-exempel&lt;br /&gt;
;; ========================================&lt;br /&gt;
(define xor-data&lt;br /&gt;
  (list (list #(0 0) #(0))&lt;br /&gt;
        (list #(0 1) #(1))&lt;br /&gt;
        (list #(1 0) #(1))&lt;br /&gt;
        (list #(1 1) #(0))))&lt;br /&gt;
&lt;br /&gt;
(define net (make-nn 2 5 1))   ; 2 input, 5 dolda neuroner, 1 output&lt;br /&gt;
(define rounds 8000) ; 2000 är i minsta laget, 5000 eller hellre 8000 är bättre&lt;br /&gt;
(define backpr 0.7)&lt;br /&gt;
(printf &amp;quot;Tränar XOR-problemet i ~a rundor med backprop styrka ~a\n&amp;quot; rounds backpr)&lt;br /&gt;
(set! net (train net xor-data rounds backpr))&lt;br /&gt;
&lt;br /&gt;
(printf &amp;quot;\n=== Testresultat ===\n&amp;quot;)&lt;br /&gt;
(for ([sample xor-data])&lt;br /&gt;
  (let ([pred (vector-ref (predict net (car sample)) 0)])&lt;br /&gt;
    (printf &amp;quot;Input ~a  →  Förväntat: ~a  Predikterat: ~a\n&amp;quot;&lt;br /&gt;
            (car sample)&lt;br /&gt;
            (vector-ref (cadr sample) 0)&lt;br /&gt;
            (if (&amp;gt; pred 0.5) 1.0 0.0))))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Neural&amp;diff=1919</id>
		<title>Racket/Neural</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Neural&amp;diff=1919"/>
		<updated>2026-07-02T14:42:13Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Algoritmer]]&lt;br /&gt;
[[category:Programmering]]&lt;br /&gt;
[[category:Racket]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;&lt;br /&gt;
;; Neuralt nät implementerad i Racket&lt;br /&gt;
;; Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se&lt;br /&gt;
;;;;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;;;;; NEURALT NÄT&lt;br /&gt;
;;;&lt;br /&gt;
;; Detta program implementerar ett litet neuralt nät och tränar det på sannings-&lt;br /&gt;
;; tabellen för XOR. Efter några tusen epoker ska prediktionerna ligga extremt nära&lt;br /&gt;
;; följande:&lt;br /&gt;
;;&lt;br /&gt;
;; a,b → a XOR b&lt;br /&gt;
;; 0,0 →    0&lt;br /&gt;
;; 0,1 →    1&lt;br /&gt;
;; 1,0 →    1&lt;br /&gt;
;; 1,1 →    0&lt;br /&gt;
;;&lt;br /&gt;
;; Implementationen har två input-neuroner för a och b&lt;br /&gt;
;; det finns 5 interna (dolda neuroner) som tränas av indatat&lt;br /&gt;
;; det finns 1 output-neuron som ger svaret&lt;br /&gt;
;;&lt;br /&gt;
;;;;; Kodens uppbyggnad och de olika funktionernas användning&lt;br /&gt;
;;&lt;br /&gt;
;; sigmoid, sigmoid-deriv      Sigmoid-kurva och derivata för backpropagation&lt;br /&gt;
;;&lt;br /&gt;
;; random-matrix, met-vec-mul  Grundläggande linalg&lt;br /&gt;
;; outer-product; vec-add&lt;br /&gt;
;;&lt;br /&gt;
;; struct nn                   Nätverksstruktur, lagrar vikter och bias&lt;br /&gt;
;;&lt;br /&gt;
;; forward                     Beräknar prediktion&lt;br /&gt;
;;&lt;br /&gt;
;; train-step                  En iteration av backpropagation&lt;br /&gt;
;;&lt;br /&gt;
;; train                       Kör många epoker av in och förväntat utdata för&lt;br /&gt;
;;                             att träna modellen&lt;br /&gt;
;;&lt;br /&gt;
;; predict                     Använder nätverket på ny data!&lt;br /&gt;
;;&lt;br /&gt;
;;&lt;br /&gt;
;;;;; Viktiga delar&lt;br /&gt;
;;&lt;br /&gt;
;; * sigmoid mappar alla värden steglöst till intervallet (0,1)&lt;br /&gt;
;; * derivatan används i backpropagation för att räkna ut gradienten&lt;br /&gt;
;;&lt;br /&gt;
;; w1: vikter mellan input och hidden layer (5x2 matris)&lt;br /&gt;
;; b1: bias för hidden layer (5-vektor)&lt;br /&gt;
;; w2: viktar mellan hidden och output (1x5 matris)&lt;br /&gt;
;; b2: bias för output (1-vektor)&lt;br /&gt;
;;&lt;br /&gt;
;; Forward pass&lt;br /&gt;
;;&lt;br /&gt;
;; 1. Beräkna aktivering i hidden layer&lt;br /&gt;
;;    z1 = W1 * input + b1&lt;br /&gt;
;;    h = rho z1&lt;br /&gt;
;; 2. Beräkna output&lt;br /&gt;
;;    z2 = W2 * h +b2&lt;br /&gt;
;;    output = rho(z2)&lt;br /&gt;
;;&lt;br /&gt;
;; Backpropagation (uppdatering av viktningen)&lt;br /&gt;
;;&lt;br /&gt;
;; Hjärtat i träningen av ett neuralt nät. Algorimen är &amp;quot;gradient descent&amp;quot; med batch-&lt;br /&gt;
;; storlek 1.&lt;br /&gt;
;;&lt;br /&gt;
;; 1. Forward pass hidden → output&lt;br /&gt;
;; 2. Output-felet&lt;br /&gt;
;;    delta = (output - target) X rho&#039; (output)&lt;br /&gt;
;; 3. Hidden-felet&lt;br /&gt;
;;    delta hidden = (W2/T * delta out) X rho&#039; (hidden)&lt;br /&gt;
;; 4. Uppdatera vikternas gradienter&lt;br /&gt;
;;    W2 ← W2 - nabla * delta out * h^T&lt;br /&gt;
;;    W1 ← W1 - nabla * delta hidden * input^T&lt;br /&gt;
;; 5. Bias uppdateras på samma sätt (jo det är en genväg)&lt;br /&gt;
;;&lt;br /&gt;
&lt;br /&gt;
(require math/base)&lt;br /&gt;
&lt;br /&gt;
;; ========================================&lt;br /&gt;
;; Aktiveringsfunktioner&lt;br /&gt;
;; ========================================&lt;br /&gt;
(define (sigmoid x)&lt;br /&gt;
  (/ 1.0 (+ 1.0 (exp (- x)))))&lt;br /&gt;
&lt;br /&gt;
(define (sigmoid-deriv y)   ; y måste redan vara sigmoid(x)&lt;br /&gt;
  (* y (- 1.0 y)))&lt;br /&gt;
&lt;br /&gt;
;; ========================================&lt;br /&gt;
;; Matris-/vektorhjälp (enkel version)&lt;br /&gt;
;; ========================================&lt;br /&gt;
(define (random-matrix rows cols)&lt;br /&gt;
  (build-vector rows&lt;br /&gt;
                (λ (_) (build-vector cols (λ (_) (- (* 2.0 (random)) 1.0))))))&lt;br /&gt;
&lt;br /&gt;
(define (vec-add v1 v2)     ; elementvis addition av två vektorer&lt;br /&gt;
  (build-vector (vector-length v1)&lt;br /&gt;
                (λ (i) (+ (vector-ref v1 i) (vector-ref v2 i)))))&lt;br /&gt;
&lt;br /&gt;
(define (scalar-mul s v)&lt;br /&gt;
  (build-vector (vector-length v) (λ (i) (* s (vector-ref v i)))))&lt;br /&gt;
&lt;br /&gt;
(define (outer-product v1 v2)   ; v1 kolumn × v2 rad → matris&lt;br /&gt;
  (build-vector (vector-length v1)&lt;br /&gt;
                (λ (i)&lt;br /&gt;
                  (build-vector (vector-length v2)&lt;br /&gt;
                                (λ (j) (* (vector-ref v1 i) (vector-ref v2 j)))))))&lt;br /&gt;
&lt;br /&gt;
(define (mat-vec-mul M v)       ; M (rows×cols) × v (cols)&lt;br /&gt;
  (build-vector (vector-length M)&lt;br /&gt;
                (λ (i)&lt;br /&gt;
                  (for/sum ([j (in-range (vector-length v))])&lt;br /&gt;
                    (* (vector-ref (vector-ref M i) j)&lt;br /&gt;
                       (vector-ref v j))))))&lt;br /&gt;
&lt;br /&gt;
(define (transpose M)&lt;br /&gt;
  (build-vector (vector-length (vector-ref M 0))&lt;br /&gt;
                (λ (j)&lt;br /&gt;
                  (build-vector (vector-length M)&lt;br /&gt;
                                (λ (i) (vector-ref (vector-ref M i) j))))))&lt;br /&gt;
&lt;br /&gt;
;; ======================================== &lt;br /&gt;
;; Neuralt nätverk&lt;br /&gt;
;; ========================================&lt;br /&gt;
(struct nn (w1 b1 w2 b2) #:transparent)&lt;br /&gt;
&lt;br /&gt;
(define (make-nn in-size hidden-size out-size)&lt;br /&gt;
  (nn (random-matrix hidden-size in-size)&lt;br /&gt;
      (make-vector hidden-size 0.0)&lt;br /&gt;
      (random-matrix out-size hidden-size)&lt;br /&gt;
      (make-vector out-size 0.0)))&lt;br /&gt;
&lt;br /&gt;
(define (forward net input)          ; input är en vektor t.ex. #(0 0)&lt;br /&gt;
  (let* ([z1 (vec-add (mat-vec-mul (nn-w1 net) input)&lt;br /&gt;
                      (nn-b1 net))]&lt;br /&gt;
         [h  (build-vector (vector-length z1) (λ (i) (sigmoid (vector-ref z1 i))))]&lt;br /&gt;
         [z2 (vec-add (mat-vec-mul (nn-w2 net) h)&lt;br /&gt;
                      (nn-b2 net))]&lt;br /&gt;
         [o  (build-vector (vector-length z2) (λ (i) (sigmoid (vector-ref z2 i))))])&lt;br /&gt;
    (values o h)))&lt;br /&gt;
&lt;br /&gt;
(define (train-step net input target lr)&lt;br /&gt;
  (let-values ([(output hidden) (forward net input)])&lt;br /&gt;
    (let* ([out-err (build-vector (vector-length output)&lt;br /&gt;
                                  (λ (i) (- (vector-ref output i)&lt;br /&gt;
                                            (vector-ref target i))))]&lt;br /&gt;
           [out-delta (build-vector (vector-length output)&lt;br /&gt;
                                    (λ (i) (* (vector-ref out-err i)&lt;br /&gt;
                                              (sigmoid-deriv (vector-ref output i)))))]&lt;br /&gt;
&lt;br /&gt;
           [hidden-err (mat-vec-mul (transpose (nn-w2 net)) out-delta)]&lt;br /&gt;
           [hidden-delta (build-vector (vector-length hidden)&lt;br /&gt;
                                       (λ (i) (* (vector-ref hidden-err i)&lt;br /&gt;
                                                 (sigmoid-deriv (vector-ref hidden i)))))]&lt;br /&gt;
&lt;br /&gt;
           ;; Uppdateringar&lt;br /&gt;
           [dw2 (outer-product out-delta hidden)]&lt;br /&gt;
           [new-w2 (build-vector (vector-length (nn-w2 net))&lt;br /&gt;
                                 (λ (i)&lt;br /&gt;
                                   (build-vector (vector-length (vector-ref (nn-w2 net) i))&lt;br /&gt;
                                                 (λ (j) (- (vector-ref (vector-ref (nn-w2 net) i) j)&lt;br /&gt;
                                                           (* lr (vector-ref (vector-ref dw2 i) j)))))))]&lt;br /&gt;
           [new-b2 (build-vector (vector-length (nn-b2 net))&lt;br /&gt;
                                 (λ (i) (- (vector-ref (nn-b2 net) i)&lt;br /&gt;
                                           (* lr (vector-ref out-delta i)))))]&lt;br /&gt;
&lt;br /&gt;
           [dw1 (outer-product hidden-delta input)]&lt;br /&gt;
           [new-w1 (build-vector (vector-length (nn-w1 net))&lt;br /&gt;
                                 (λ (i)&lt;br /&gt;
                                   (build-vector (vector-length (vector-ref (nn-w1 net) i))&lt;br /&gt;
                                                 (λ (j) (- (vector-ref (vector-ref (nn-w1 net) i) j)&lt;br /&gt;
                                                           (* lr (vector-ref (vector-ref dw1 i) j)))))))]&lt;br /&gt;
           [new-b1 (build-vector (vector-length (nn-b1 net))&lt;br /&gt;
                                 (λ (i) (- (vector-ref (nn-b1 net) i)&lt;br /&gt;
                                           (* lr (vector-ref hidden-delta i)))))])&lt;br /&gt;
&lt;br /&gt;
      (nn new-w1 new-b1 new-w2 new-b2))))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; ========================================&lt;br /&gt;
;; Skriv ut vikter och bias – snygg tabell (fixad version)&lt;br /&gt;
;; ========================================&lt;br /&gt;
(define (print-nn net)&lt;br /&gt;
  (printf &amp;quot;\n=== NEURALT NÄTVERK - VIKTER OCH BIAS ===\n\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  ;; Hjälpfunktion för att formatera tal snyggt (med minustecken på rätt plats)&lt;br /&gt;
  (define (fmt x)&lt;br /&gt;
    (~r x &lt;br /&gt;
        #:min-width 10 &lt;br /&gt;
        #:precision &#039;(= 4)&lt;br /&gt;
        #:sign #f))          ; #f = visa minus för negativa, inget tecken för positiva&lt;br /&gt;
&lt;br /&gt;
  ;; W1: Vikter från input till hidden (5×2)&lt;br /&gt;
  (printf &amp;quot;W1 (Hidden × Input):\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;          Input0      Input1\n&amp;quot;)&lt;br /&gt;
  (for ([i (in-range (vector-length (nn-w1 net)))])&lt;br /&gt;
    (printf &amp;quot;Hidden~a  &amp;quot; i)&lt;br /&gt;
    (for ([j (in-range 2)])&lt;br /&gt;
      (printf &amp;quot;~a  &amp;quot; (fmt (vector-ref (vector-ref (nn-w1 net) i) j))))&lt;br /&gt;
    (printf &amp;quot;\n&amp;quot;))&lt;br /&gt;
  (printf &amp;quot;\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  ;; b1: Bias för hidden layer&lt;br /&gt;
  (printf &amp;quot;b1 (Bias hidden layer):\n&amp;quot;)&lt;br /&gt;
  (for ([i (in-range (vector-length (nn-b1 net)))])&lt;br /&gt;
    (printf &amp;quot;~a  &amp;quot; (fmt (vector-ref (nn-b1 net) i))))&lt;br /&gt;
  (printf &amp;quot;\n\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  ;; W2: Vikter från hidden till output (1×5)&lt;br /&gt;
  (printf &amp;quot;W2 (Output × Hidden):\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;         Hidden0     Hidden1     Hidden2     Hidden3     Hidden4\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;Output   &amp;quot;)&lt;br /&gt;
  (for ([j (in-range 5)])&lt;br /&gt;
    (printf &amp;quot;~a  &amp;quot; (fmt (vector-ref (vector-ref (nn-w2 net) 0) j))))&lt;br /&gt;
  (printf &amp;quot;\n\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  ;; b2: Bias för output&lt;br /&gt;
  (printf &amp;quot;b2 (Bias output):\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;~a\n&amp;quot; (fmt (vector-ref (nn-b2 net) 0)))&lt;br /&gt;
&lt;br /&gt;
  (printf &amp;quot;\n========================================\n&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Träna nätverket&lt;br /&gt;
(define (train net data epochs lr)&lt;br /&gt;
  (for ([ep (in-range epochs)])&lt;br /&gt;
    (for ([sample data])&lt;br /&gt;
      (set! net (train-step net (car sample) (cadr sample) lr)))&lt;br /&gt;
    &lt;br /&gt;
    (when (zero? (modulo ep 500))      ; oftare än var 1000:e&lt;br /&gt;
      (let ([loss (for/sum ([sample data])&lt;br /&gt;
                    (let-values ([(output _) (forward net (car sample))])&lt;br /&gt;
                      (let ([target (vector-ref (cadr sample) 0)]&lt;br /&gt;
                            [pred   (vector-ref output 0)])&lt;br /&gt;
                        (sqr (- target pred)))))])&lt;br /&gt;
        (printf &amp;quot;Epoch ~a  Loss: ~a\n&amp;quot; ep (/ loss (length data))))))&lt;br /&gt;
  (print-nn net)&lt;br /&gt;
  net)&lt;br /&gt;
&lt;br /&gt;
;; Prediktera&lt;br /&gt;
(define (predict net input)&lt;br /&gt;
  (let-values ([(output _) (forward net input)])&lt;br /&gt;
    output))&lt;br /&gt;
&lt;br /&gt;
;; ========================================&lt;br /&gt;
;; XOR-exempel&lt;br /&gt;
;; ========================================&lt;br /&gt;
(define xor-data&lt;br /&gt;
  (list (list #(0 0) #(0))&lt;br /&gt;
        (list #(0 1) #(1))&lt;br /&gt;
        (list #(1 0) #(1))&lt;br /&gt;
        (list #(1 1) #(0))))&lt;br /&gt;
&lt;br /&gt;
(define net (make-nn 2 5 1))   ; 2 input, 5 dolda neuroner, 1 output&lt;br /&gt;
(define rounds 8000) ; 2000 är i minsta laget, 5000 eller hellre 8000 är bättre&lt;br /&gt;
(define backpr 0.7)&lt;br /&gt;
(printf &amp;quot;Tränar XOR-problemet i ~a rundor med backprop styrka ~a\n&amp;quot; rounds backpr)&lt;br /&gt;
(set! net (train net xor-data rounds backpr))&lt;br /&gt;
&lt;br /&gt;
(printf &amp;quot;\n=== Testresultat ===\n&amp;quot;)&lt;br /&gt;
(for ([sample xor-data])&lt;br /&gt;
  (let ([pred (vector-ref (predict net (car sample)) 0)])&lt;br /&gt;
    (printf &amp;quot;Input ~a  →  Förväntat: ~a  Predikterat: ~a\n&amp;quot;&lt;br /&gt;
            (car sample)&lt;br /&gt;
            (vector-ref (cadr sample) 0)&lt;br /&gt;
            (if (&amp;gt; pred 0.5) 1.0 0.0))))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1918</id>
		<title>Racket/Erastothenes</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1918"/>
		<updated>2026-06-29T15:52:10Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;&lt;br /&gt;
;; Eratosthenes såll implementerad i Racket&lt;br /&gt;
;; Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se&lt;br /&gt;
;;;;;&lt;br /&gt;
&lt;br /&gt;
(require racket/format)&lt;br /&gt;
&lt;br /&gt;
;; Skapar en vektor där indexet motsvarar talet.&lt;br /&gt;
;; Om (vector-ref v n) är #t, så är n ett primtal.&lt;br /&gt;
(define (make-sieve limit)&lt;br /&gt;
  ;; 1 SKAPA VEKTORN&lt;br /&gt;
  ;; Vi skapar en vektor med storleken (limit + 1). Eftersom index börjar på 0,&lt;br /&gt;
  ;; måste vektorn vara ett element större än &#039;limit&#039; för att &#039;limit&#039; ska få plats som sista index.&lt;br /&gt;
  ;; Vi antar från början att ALLA tal är primtal (#t), och sållar sedan bort de som inte är det.&lt;br /&gt;
  (define sieve (make-vector (add1 limit) #t))&lt;br /&gt;
  &lt;br /&gt;
  ;; 2 BASFALL (0 OCH 1)&lt;br /&gt;
  ;; 0 och 1 är aldrig primtal enligt definitionen (ett primtal måste vara större än 1).&lt;br /&gt;
  ;; Om användaren har angett en limit som inkluderar dessa, markerar vi dem direkt som #f (falskt).&lt;br /&gt;
  (when (&amp;gt;= limit 0) (vector-set! sieve 0 #f))&lt;br /&gt;
  (when (&amp;gt;= limit 1) (vector-set! sieve 1 #f))&lt;br /&gt;
  &lt;br /&gt;
  ;; 3 HUVUDLOOPEN (YTTRE)&lt;br /&gt;
  ;; Vi behöver bara gå upp till kvadratroten av limit (avrundat nedåt till heltal).&lt;br /&gt;
  ;; Varför? Om ett tal har en faktor som är större än kvadratroten, måste dess &lt;br /&gt;
  ;; motsvarande partnerfaktor vara mindre än kvadratroten. Har vi redan rensat&lt;br /&gt;
  ;; alla mindre faktorer så har vi automatiskt tagit hand om de större!&lt;br /&gt;
  (for ((p (in-range 2 (add1 (integer-sqrt limit)))))&lt;br /&gt;
    &lt;br /&gt;
    ;; Om index &#039;p&#039; fortfarande är #t, så har vi hittat ett primtal.&lt;br /&gt;
    (when (vector-ref sieve p)&lt;br /&gt;
      &lt;br /&gt;
      ;; 4 SÅLLNINGSLOOPEN (INRE)&lt;br /&gt;
      ;; Markera alla multiplar av p som falska (inte primtal).&lt;br /&gt;
      ;; Optimering: Vi startar direkt vid (* p p) istället för (* p 2).&lt;br /&gt;
      ;; Varför? Alla multiplar mindre än p*p (som 2p, 3p, 4p... upp till (p-1)p) &lt;br /&gt;
      ;; har redan markerats som #f i tidigare varv av den yttre loopen.&lt;br /&gt;
      ;; Det tredje argumentet &#039;p&#039; gör att loopen hoppar med p-steg i taget (p*p, p*p+p, p*p+2p, osv).&lt;br /&gt;
      (for ((multiple (in-range (* p p) (add1 limit) p)))&lt;br /&gt;
        (vector-set! sieve multiple #f)))) ; inte primtal&lt;br /&gt;
        &lt;br /&gt;
  ;; Returnera det färdiga sållet (vektorn) till anroparen&lt;br /&gt;
  sieve)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Returnerar en lista med alla primtal mellan start och stop&lt;br /&gt;
(define (primes-between start stop)&lt;br /&gt;
  ;; Kontrollera om intervallet är logiskt. Om start är större än stop finns det inga tal.&lt;br /&gt;
  (if (&amp;gt; start stop)&lt;br /&gt;
      &#039;() ; Returnerar tomt om start &amp;gt; stop&lt;br /&gt;
      &lt;br /&gt;
      ;; Skapa sållet för hela intervallet upp till &#039;stop&#039;.&lt;br /&gt;
      (let ((is-prime? (make-sieve stop)))&lt;br /&gt;
        &lt;br /&gt;
        ;; for/list samlar automatiskt ihop alla &#039;n&#039; som matchar villkoret till en lista.&lt;br /&gt;
        ;; (max start 2) förhindrar att vi letar efter primtal under 2, då de inte finns.&lt;br /&gt;
        (for/list ((n (in-range (max start 2) (add1 stop)))&lt;br /&gt;
                   #:when (vector-ref is-prime? n)) ; Villkor: Endast om index n är #t i sållet&lt;br /&gt;
          n))))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Hjälpmakro för att tajma hur lång tid operationen tog&lt;br /&gt;
(define-syntax-rule (timeit expr)&lt;br /&gt;
  ;; Spara starttiden i millisekunder innan uttrycket körs&lt;br /&gt;
  (let ((start (current-inexact-milliseconds)))&lt;br /&gt;
    ;; Kör uttrycket &#039;expr&#039; (t.ex. vår funktion) och spara resultatet i &#039;result&#039;&lt;br /&gt;
    (let ((result expr))&lt;br /&gt;
      ;; Skriv ut tidsskillnaden med snygg lokal formatering&lt;br /&gt;
      (printf &amp;quot;Tid: ~a ms\n&amp;quot; (~r (- (current-inexact-milliseconds) start)&lt;br /&gt;
                                #:group-sep   &amp;quot; &amp;quot;     ; tusentalsavskiljare (blanksteg)&lt;br /&gt;
                                #:decimal-sep &amp;quot;,&amp;quot;     ; decimalsavskiljare (komma)&lt;br /&gt;
                                #:precision     2))   ; avrunda till 2 decimaler&lt;br /&gt;
      result)))  ; Returnera svaret från expr så att programmet kan använda det&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Testkörning&lt;br /&gt;
(timeit (primes-between 10000000 10000500))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1917</id>
		<title>Racket/Erastothenes</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1917"/>
		<updated>2026-06-29T15:51:49Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;&lt;br /&gt;
;; Eratosthenes såll implementerad i Racket&lt;br /&gt;
;; Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se&lt;br /&gt;
;;;;;&lt;br /&gt;
&lt;br /&gt;
(require racket/format)&lt;br /&gt;
&lt;br /&gt;
;; Skapar en vektor där indexet motsvarar talet.&lt;br /&gt;
;; Om (vector-ref v n) är #t, så är n ett primtal.&lt;br /&gt;
(define (make-sieve limit)&lt;br /&gt;
  ;; 1 SKAPA VEKTORN&lt;br /&gt;
  ;; Vi skapar en vektor med storleken (limit + 1). Eftersom index börjar på 0,&lt;br /&gt;
  ;; måste vektorn vara ett element större än &#039;limit&#039; för att &#039;limit&#039; ska få plats som sista index.&lt;br /&gt;
  ;; Vi antar från början att ALLA tal är primtal (#t), och sållar sedan bort de som inte är det.&lt;br /&gt;
  (define sieve (make-vector (add1 limit) #t))&lt;br /&gt;
  &lt;br /&gt;
  ;; 2 BASFALL (0 OCH 1)&lt;br /&gt;
  ;; 0 och 1 är aldrig primtal enligt definitionen (ett primtal måste vara större än 1).&lt;br /&gt;
  ;; Om användaren har angett en limit som inkluderar dessa, markerar vi dem direkt som #f (falskt).&lt;br /&gt;
  (when (&amp;gt;= limit 0) (vector-set! sieve 0 #f))&lt;br /&gt;
  (when (&amp;gt;= limit 1) (vector-set! sieve 1 #f))&lt;br /&gt;
  &lt;br /&gt;
  ;; 3 HUVUDLOOPEN (YTTRE)&lt;br /&gt;
  ;; Vi behöver bara gå upp till kvadratroten av limit (avrundat nedåt till heltal).&lt;br /&gt;
  ;; Varför? Om ett tal har en faktor som är större än kvadratroten, måste dess &lt;br /&gt;
  ;; motsvarande partnerfaktor vara mindre än kvadratroten. Har vi redan rensat&lt;br /&gt;
  ;; alla mindre faktorer så har vi automatiskt tagit hand om de större!&lt;br /&gt;
  (for ((p (in-range 2 (add1 (integer-sqrt limit)))))&lt;br /&gt;
    &lt;br /&gt;
    ;; Om index &#039;p&#039; fortfarande är #t, så har vi hittat ett primtal.&lt;br /&gt;
    (when (vector-ref sieve p)&lt;br /&gt;
      &lt;br /&gt;
      ;; 4 SÅLLNINGSLOOPEN (INRE)&lt;br /&gt;
      ;; Markera alla multiplar av p som falska (inte primtal).&lt;br /&gt;
      ;; Optimering: Vi startar direkt vid (* p p) istället för (* p 2).&lt;br /&gt;
      ;; Varför? Alla multiplar mindre än p*p (som 2p, 3p, 4p... upp till (p-1)p) &lt;br /&gt;
      ;; har redan markerats som #f i tidigare varv av den yttre loopen.&lt;br /&gt;
      ;; Det tredje argumentet &#039;p&#039; gör att loopen hoppar med p-steg i taget (p*p, p*p+p, p*p+2p, osv).&lt;br /&gt;
      (for ((multiple (in-range (* p p) (add1 limit) p)))&lt;br /&gt;
        (vector-set! sieve multiple #f)))) ; inte primtal&lt;br /&gt;
        &lt;br /&gt;
  ;; Returnera det färdiga sållet (vektorn) till anroparen&lt;br /&gt;
  sieve)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Returnerar en lista med alla primtal mellan start och stop&lt;br /&gt;
(define (primes-between start stop)&lt;br /&gt;
  ;; Kontrollera om intervallet är logiskt. Om start är större än stop finns det inga tal.&lt;br /&gt;
  (if (&amp;gt; start stop)&lt;br /&gt;
      &#039;() ; Returnerar tomt om start &amp;gt; stop&lt;br /&gt;
      &lt;br /&gt;
      ;; Skapa sållet för hela intervallet upp till &#039;stop&#039;.&lt;br /&gt;
      (let ((is-prime? (make-sieve stop)))&lt;br /&gt;
        &lt;br /&gt;
        ;; for/list samlar automatiskt ihop alla &#039;n&#039; som matchar villkoret till en lista.&lt;br /&gt;
        ;; (max start 2) förhindrar att vi letar efter primtal under 2, då de inte finns.&lt;br /&gt;
        (for/list ((n (in-range (max start 2) (add1 stop)))&lt;br /&gt;
                   #:when (vector-ref is-prime? n)) ; Villkor: Endast om index n är #t i sållet&lt;br /&gt;
          n))))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Hjälpmakro för att tajma hur lång tid operationen tog&lt;br /&gt;
(define-syntax-rule (timeit expr)&lt;br /&gt;
  ;; Spara starttiden i millisekunder innan uttrycket körs&lt;br /&gt;
  (let ((start (current-inexact-milliseconds)))&lt;br /&gt;
    ;; Kör uttrycket &#039;expr&#039; (t.ex. vår funktion) och spara resultatet i &#039;result&#039;&lt;br /&gt;
    (let ((result expr))&lt;br /&gt;
      ;; Skriv ut tidsskillnaden med snygg lokal formatering&lt;br /&gt;
      (printf &amp;quot;Tid: ~a ms\n&amp;quot; (~r (- (current-inexact-milliseconds) start)&lt;br /&gt;
                                #:group-sep   &amp;quot; &amp;quot;     ; tusentalsavskiljare (blanksteg)&lt;br /&gt;
                                #:decimal-sep &amp;quot;,&amp;quot;     ; decimalsavskiljare (komma)&lt;br /&gt;
                                #:precision     2))   ; avrunda till 2 decimaler&lt;br /&gt;
      result)))  ; Returnera svaret från expr så att programmet kan använda det&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Testkörning&lt;br /&gt;
(timeit (primes-between 10000000 10000500))&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1916</id>
		<title>Racket/Erastothenes</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1916"/>
		<updated>2026-06-29T15:51:17Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;&lt;br /&gt;
;; Eratosthenes såll implementerad i Racket&lt;br /&gt;
;; Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se&lt;br /&gt;
;;;;;&lt;br /&gt;
&lt;br /&gt;
(require racket/format)&lt;br /&gt;
&lt;br /&gt;
;; Skapar en vektor där indexet motsvarar talet.&lt;br /&gt;
;; Om (vector-ref v n) är #t, så är n ett primtal.&lt;br /&gt;
(define (make-sieve limit)&lt;br /&gt;
  ;; 1. SKAPA VEKTORN&lt;br /&gt;
  ;; Vi skapar en vektor med storleken (limit + 1). Eftersom index börjar på 0,&lt;br /&gt;
  ;; måste vektorn vara ett element större än &#039;limit&#039; för att &#039;limit&#039; ska få plats som sista index.&lt;br /&gt;
  ;; Vi antar från början att ALLA tal är primtal (#t), och sållar sedan bort de som inte är det.&lt;br /&gt;
  (define sieve (make-vector (add1 limit) #t))&lt;br /&gt;
  &lt;br /&gt;
  ;; 2. BASFALL (0 OCH 1)&lt;br /&gt;
  ;; 0 och 1 är aldrig primtal enligt definitionen (ett primtal måste vara större än 1).&lt;br /&gt;
  ;; Om användaren har angett en limit som inkluderar dessa, markerar vi dem direkt som #f (falskt).&lt;br /&gt;
  (when (&amp;gt;= limit 0) (vector-set! sieve 0 #f))&lt;br /&gt;
  (when (&amp;gt;= limit 1) (vector-set! sieve 1 #f))&lt;br /&gt;
  &lt;br /&gt;
  ;; 3. HUVUDLOOPEN (YTTRE)&lt;br /&gt;
  ;; Vi behöver bara gå upp till kvadratroten av limit (avrundat nedåt till heltal).&lt;br /&gt;
  ;; Varför? Om ett tal har en faktor som är större än kvadratroten, måste dess &lt;br /&gt;
  ;; motsvarande partnerfaktor vara mindre än kvadratroten. Har vi redan rensat&lt;br /&gt;
  ;; alla mindre faktorer så har vi automatiskt tagit hand om de större!&lt;br /&gt;
  (for ((p (in-range 2 (add1 (integer-sqrt limit)))))&lt;br /&gt;
    &lt;br /&gt;
    ;; Om index &#039;p&#039; fortfarande är #t, så har vi hittat ett primtal.&lt;br /&gt;
    (when (vector-ref sieve p)&lt;br /&gt;
      &lt;br /&gt;
      ;; 4. SÅLLNINGSLOOPEN (INRE)&lt;br /&gt;
      ;; Markera alla multiplar av p som falska (inte primtal).&lt;br /&gt;
      ;; Optimering: Vi startar direkt vid (* p p) istället för (* p 2).&lt;br /&gt;
      ;; Varför? Alla multiplar mindre än p*p (som 2p, 3p, 4p... upp till (p-1)p) &lt;br /&gt;
      ;; har redan markerats som #f i tidigare varv av den yttre loopen.&lt;br /&gt;
      ;; Det tredje argumentet &#039;p&#039; gör att loopen hoppar med p-steg i taget (p*p, p*p+p, p*p+2p, osv).&lt;br /&gt;
      (for ((multiple (in-range (* p p) (add1 limit) p)))&lt;br /&gt;
        (vector-set! sieve multiple #f)))) ; inte primtal&lt;br /&gt;
        &lt;br /&gt;
  ;; Returnera det färdiga sållet (vektorn) till anroparen&lt;br /&gt;
  sieve)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Returnerar en lista med alla primtal mellan start och stop&lt;br /&gt;
(define (primes-between start stop)&lt;br /&gt;
  ;; Kontrollera om intervallet är logiskt. Om start är större än stop finns det inga tal.&lt;br /&gt;
  (if (&amp;gt; start stop)&lt;br /&gt;
      &#039;() ; Returnerar tomt om start &amp;gt; stop&lt;br /&gt;
      &lt;br /&gt;
      ;; Skapa sållet för hela intervallet upp till &#039;stop&#039;.&lt;br /&gt;
      (let ((is-prime? (make-sieve stop)))&lt;br /&gt;
        &lt;br /&gt;
        ;; for/list samlar automatiskt ihop alla &#039;n&#039; som matchar villkoret till en lista.&lt;br /&gt;
        ;; (max start 2) förhindrar att vi letar efter primtal under 2, då de inte finns.&lt;br /&gt;
        (for/list ((n (in-range (max start 2) (add1 stop)))&lt;br /&gt;
                   #:when (vector-ref is-prime? n)) ; Villkor: Endast om index n är #t i sållet&lt;br /&gt;
          n))))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Hjälpmakro för att tajma hur lång tid operationen tog&lt;br /&gt;
(define-syntax-rule (timeit expr)&lt;br /&gt;
  ;; Spara starttiden i millisekunder innan uttrycket körs&lt;br /&gt;
  (let ((start (current-inexact-milliseconds)))&lt;br /&gt;
    ;; Kör uttrycket &#039;expr&#039; (t.ex. vår funktion) och spara resultatet i &#039;result&#039;&lt;br /&gt;
    (let ((result expr))&lt;br /&gt;
      ;; Skriv ut tidsskillnaden med snygg lokal formatering&lt;br /&gt;
      (printf &amp;quot;Tid: ~a ms\n&amp;quot; (~r (- (current-inexact-milliseconds) start)&lt;br /&gt;
                                #:group-sep   &amp;quot; &amp;quot;     ; tusentalsavskiljare (blanksteg)&lt;br /&gt;
                                #:decimal-sep &amp;quot;,&amp;quot;     ; decimalsavskiljare (komma)&lt;br /&gt;
                                #:precision     2))   ; avrunda till 2 decimaler&lt;br /&gt;
      result)))  ; Returnera svaret från expr så att programmet kan använda det&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Testkörning&lt;br /&gt;
(timeit (primes-between 10000000 10000500))&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1915</id>
		<title>Racket/Erastothenes</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1915"/>
		<updated>2026-06-29T15:42:26Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Algoritmer]]&lt;br /&gt;
[[category:Programmering]]&lt;br /&gt;
[[category:Racket]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;&lt;br /&gt;
;; Erastothenes såll implementerad i Racket&lt;br /&gt;
;; Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se&lt;br /&gt;
;;;;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Skapar en vektor där indexet motsvarar talet.&lt;br /&gt;
;; Om (vector-ref v n) är #t, så är n ett primtal.&lt;br /&gt;
&lt;br /&gt;
(define (make-sieve limit)&lt;br /&gt;
  (define sieve (make-vector (add1 limit) #t))&lt;br /&gt;
  &lt;br /&gt;
  ;; 0 och 1 är aldrig primtal&lt;br /&gt;
  ;; Primtal är tal som är större än 1 och endast jämt delbara med&lt;br /&gt;
  ;; sig själv eller 1. Så börja med att markera 0 och 1 som icke-prim.&lt;br /&gt;
  (when (&amp;gt;= limit 0) (vector-set! sieve 0 #f))&lt;br /&gt;
  (when (&amp;gt;= limit 1) (vector-set! sieve 1 #f))&lt;br /&gt;
  &lt;br /&gt;
  ;; Vi behöver bara gå upp till kvadratroten av limit&lt;br /&gt;
  ;; p går från start till stopp och markerar alla multiplar som &lt;br /&gt;
  ;; icke prima. Dvs 2, 4, 6, 8, 10, 12 osv (alla multiplar av 2)&lt;br /&gt;
  ;; sedan 3, 6, 9, 12, 15 osv (alla multiplar av 3) etc&lt;br /&gt;
  ;; det som inte är markera när vi når sqrt(limit)+1 är alltså primtal&lt;br /&gt;
  (for ((p (in-range 2 (add1 (integer-sqrt limit)))))&lt;br /&gt;
    (when (vector-ref sieve p)&lt;br /&gt;
      ;; Markera alla multiplar av p som falska (starta vid p*p)&lt;br /&gt;
      (for ((multiple (in-range (* p p) (add1 limit) p)))&lt;br /&gt;
        (vector-set! sieve multiple #f)))) ; inte primtal&lt;br /&gt;
  ;; returnera sållet till anroparen&lt;br /&gt;
  sieve)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Returnerar en lista med alla primtal mellan start och stop&lt;br /&gt;
(define (primes-between start stop)&lt;br /&gt;
  (if (&amp;gt; start stop)&lt;br /&gt;
      &#039;() ;Returnerar tomt om start &amp;gt; stop&lt;br /&gt;
      (let ([is-prime? (make-sieve stop)])&lt;br /&gt;
        (for/list ([n (in-range (max start 2) (add1 stop))]&lt;br /&gt;
                   #:when (vector-ref is-prime? n))&lt;br /&gt;
          n))))&lt;br /&gt;
&lt;br /&gt;
;; Hjälpmakro för att tajma hur lång tid operationen tog&lt;br /&gt;
(require racket/format)&lt;br /&gt;
(define-syntax-rule (timeit expr)&lt;br /&gt;
  (let ((start (current-inexact-milliseconds)))&lt;br /&gt;
    (let ((result expr))&lt;br /&gt;
      (printf &amp;quot;Tid: ~a ms\n&amp;quot; (~r (- (current-inexact-milliseconds) start)&lt;br /&gt;
              #:group-sep &amp;quot; &amp;quot;     ;tusentalsavskiljare&lt;br /&gt;
              #:decimal-sep &amp;quot;,&amp;quot;   ;decimalsavskiljare&lt;br /&gt;
              #:precision 2))     ;antal decimaler&lt;br /&gt;
      result)))  ; returnera svaret&lt;br /&gt;
&lt;br /&gt;
;; Testkörning&lt;br /&gt;
(timeit (primes-between 10000000 10000100))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1914</id>
		<title>Racket/Erastothenes</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Erastothenes&amp;diff=1914"/>
		<updated>2026-06-29T14:31:10Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Algoritmer]]&lt;br /&gt;
[[category:Programmering]]&lt;br /&gt;
[[category:Racket]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;;;;;&lt;br /&gt;
;; Erastothenes såll implementerad i Racket&lt;br /&gt;
;; Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se&lt;br /&gt;
;;;;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Skapar en vektor där indexet motsvarar talet.&lt;br /&gt;
;; Om (vector-ref v n) är #t, så är n ett primtal.&lt;br /&gt;
&lt;br /&gt;
(define (make-sieve limit)&lt;br /&gt;
  (define sieve (make-vector (add1 limit) #t))&lt;br /&gt;
  &lt;br /&gt;
  ;; 0 och 1 är aldrig primtal&lt;br /&gt;
  (when (&amp;gt;= limit 0) (vector-set! sieve 0 #f))&lt;br /&gt;
  (when (&amp;gt;= limit 1) (vector-set! sieve 1 #f))&lt;br /&gt;
  &lt;br /&gt;
  ;; Vi behöver bara gå upp till kvadratroten av limit&lt;br /&gt;
  (for ([p (in-range 2 (add1 (integer-sqrt limit)))])&lt;br /&gt;
    (when (vector-ref sieve p)&lt;br /&gt;
      ;; Markera alla multiplar av p som falska (starta vid p*p)&lt;br /&gt;
      (for ([multiple (in-range (* p p) (add1 limit) p)])&lt;br /&gt;
        (vector-set! sieve multiple #f))))&lt;br /&gt;
  ;; returnera sållet till anroparen&lt;br /&gt;
  sieve)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;; Returnerar en lista med alla primtal mellan start och stop&lt;br /&gt;
(define (primes-between start stop)&lt;br /&gt;
  (if (&amp;gt; start stop)&lt;br /&gt;
      &#039;() ;Returnerar tomt om start &amp;gt; stop&lt;br /&gt;
      (let ([is-prime? (make-sieve stop)])&lt;br /&gt;
        (for/list ([n (in-range (max start 2) (add1 stop))]&lt;br /&gt;
                   #:when (vector-ref is-prime? n))&lt;br /&gt;
          n))))&lt;br /&gt;
&lt;br /&gt;
;; Hjälpmakro för att tajma hur lång tid operationen tog&lt;br /&gt;
(require racket/format)&lt;br /&gt;
(define-syntax-rule (timeit expr)&lt;br /&gt;
  (let ((start (current-inexact-milliseconds)))&lt;br /&gt;
    (let ((result expr))&lt;br /&gt;
      (printf &amp;quot;Tid: ~a ms\n&amp;quot; (~r (- (current-inexact-milliseconds) start)&lt;br /&gt;
              #:group-sep &amp;quot; &amp;quot;     ;tusentalsavskiljare&lt;br /&gt;
              #:decimal-sep &amp;quot;,&amp;quot;   ;decimalsavskiljare&lt;br /&gt;
              #:precision 2))     ;antal decimaler&lt;br /&gt;
      result)))  ; returnera svaret&lt;br /&gt;
&lt;br /&gt;
;; Testkörning&lt;br /&gt;
(timeit (primes-between 10000000 10000100))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1913</id>
		<title>Viterbi</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1913"/>
		<updated>2026-06-23T18:18:37Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:algoritmer]]&lt;br /&gt;
[[category:felkorrigering]]&lt;br /&gt;
[[category:telekom]]&lt;br /&gt;
[[category:c]]&lt;br /&gt;
[[category:signalering]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Bakgrund = &lt;br /&gt;
&lt;br /&gt;
== Viterbis algoritm – Hur vi hittar rätt signal i bruset ==&lt;br /&gt;
&lt;br /&gt;
Inom amatörradion och digital signalbehandling stöter vi ofta på problemet med brus och fading. När vi skickar digital data över etern, till exempel med modernare digitala trafiksätt eller vid satellitkommunikation, använder vi ofta något som kallas för faltningskodning (convolutional coding). Det är en metod där varje sänd bit beror på både den nuvarande biten och ett antal tidigare bitar. Detta skapar ett mönster, ett beroende, som gör signalen robust.&lt;br /&gt;
&lt;br /&gt;
Men hur avkodar vi detta på mottagarsidan när atmosfären har ställt till det och lagt till en massa brus? Det är här Viterbis algoritm kommer in i bilden.&lt;br /&gt;
&lt;br /&gt;
Kort sagt är Viterbialgoritmen en metod för att hitta den mest troliga sekvensen av dolda tillstånd (det vi faktiskt sände) baserat på en serie observerade händelser (den brusiga signalen vi tog emot). Istället för att gissa bit för bit, tittar algoritmen på hela sammanhanget.&lt;br /&gt;
&lt;br /&gt;
== Trellis-diagrammet: Vägarna genom historien ==&lt;br /&gt;
&lt;br /&gt;
För att förstå hur algoritmen jobbar kan vi tänka oss ett nätverk av vägar, ett så kallat trellis-diagram.&lt;br /&gt;
&lt;br /&gt;
* Tillstånd (States): Radiosändarens &amp;quot;minne&amp;quot; kan vid varje given tidpunkt befinna sig i ett visst antal tillstånd (beroende på de senaste sända bitarna).&lt;br /&gt;
* Övergångar: När en ny bit ska sändas hoppar sändaren till ett nytt tillstånd och skickar ut en specifik kombination av bitar i luften.&lt;br /&gt;
&lt;br /&gt;
== Hur funkar det? ==&lt;br /&gt;
&lt;br /&gt;
När mottagaren lyssnar får den in en sekvens som kanske är halvt dränkt i brus. En nolla kanske ser ut som en svag etta, och så vidare. Viterbialgoritmen börjar nu räkna på alla möjliga vägar genom detta nätverk av tillstånd över tid.&lt;br /&gt;
Så här jobbar algoritmen steg för steg:&lt;br /&gt;
&lt;br /&gt;
# Beräkna avstånd (Metric): För varje steg i tiden tittar algoritmen på de bitar som faktiskt togs emot och jämför dem med vad som borde ha sänts för varje tänkbar väg i diagrammet. Man mäter &amp;quot;avståndet&amp;quot; (ofta Hamming-avstånd för ettor och nollor, eller Euklidiskt avstånd om man kör &amp;quot;soft decision&amp;quot; där man mäter signalstyrkan mer exakt). Ju mindre avstånd, desto mer troligt är det att den vägen är den rätta.&lt;br /&gt;
# Spara den bästa vägen (Add-Compare-Select): Till varje nytt tillstånd i diagrammet leder det oftast två olika vägar från det föregående steget. Algoritmen adderar det nya avståndet till det gamla, jämför de två alternativen, och väljer den väg som har lägst totalt avstånd (fel). Den andra vägen slängs bort för alltid. Det här är algoritmens stora styrka: den rensar hela tiden bort återvändsgränder så att beräkningsbördan inte exploderar.&lt;br /&gt;
# Lås historiken (Traceback): När hela sekvensen (eller ett tillräckligt långt block) har tagits emot, tittar man på vilket slutgiltigt tillstånd som har det absolut lägsta totala felvärdet. Sedan backar man bakåt genom de sparade &amp;quot;bästa vägarna&amp;quot; till början.&lt;br /&gt;
&lt;br /&gt;
Resultatet blir den absolut mest sannolika sekvensen av bitar som sändaren faktiskt skickade ut, trots att det var fullt av bitfel under resans gång.&lt;br /&gt;
&lt;br /&gt;
= Implementation i hyggligt modern C=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Anders Sikvall 2026 Version 1.0&lt;br /&gt;
 * &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
 * &lt;br /&gt;
 * Implementation i C av en standard Viterbi-algoritm som används bl.a. av &lt;br /&gt;
 * Qualcomm och NASA och är välpublicerad.&lt;br /&gt;
 *&lt;br /&gt;
 * PUBLIC DOMAIN FOR EDUCATIONAL PURPOSES&lt;br /&gt;
 *&lt;br /&gt;
 * Kompilera med gcc -o viterbi viterbi.c&lt;br /&gt;
 *&lt;br /&gt;
 * ANVÄNDNING&lt;br /&gt;
 *         viterbi -e &amp;lt;indatastream &amp;gt;utdatastream   Kodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -d &amp;lt;indatastream &amp;gt;utdatastream   Avkodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -t                               Utför en intern test på 64 randomiserade oktetter&lt;br /&gt;
 *         viterbi -h                               Visar en kort hjälptext&lt;br /&gt;
 *         viterbi --error                          Samma sak som -t men introducerar 3 flippade bitpar&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Constraint length (K=7) innebär att skiftregistret minns de senaste 7 bitarna. */&lt;br /&gt;
#define K 7&lt;br /&gt;
&lt;br /&gt;
/* Antalet tillstånd i trellis-diagrammet bestäms av de senaste K-1 bitarna.&lt;br /&gt;
   2^(7-1) = 2^6 = 64 unika tillstånd totalt. */&lt;br /&gt;
#define NUM_STATES (1 &amp;lt;&amp;lt; (K - 1))&lt;br /&gt;
&lt;br /&gt;
/* Standard NASA/Qualcomm-polynom för Rate 1/2 konvolutionskodning.&lt;br /&gt;
   POLY_G0 motsvarar oktalt 151 (1001111 binärt).&lt;br /&gt;
   POLY_G1 motsvarar oktalt 113 (1101101 binärt).&lt;br /&gt;
   Dessa maskar används för att bestämma vilka bitar i tillståndet som XOR-sammanfogas. */&lt;br /&gt;
const uint8_t POLY_G0 = 0x4F;&lt;br /&gt;
const uint8_t POLY_G1 = 0x6D;&lt;br /&gt;
&lt;br /&gt;
/* Hårdvaruoptimerad paritetsberäkning.&lt;br /&gt;
   Räknar antalet satta bitar (1:or) i ett tal och returnerar 1 om antalet är udda,&lt;br /&gt;
   eller 0 om antalet är jämnt (motsvarar en kedja av XOR-grindar). */&lt;br /&gt;
static inline uint8_t parity(uint8_t x) {&lt;br /&gt;
    return __builtin_parity(x) &amp;amp; 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// KODARE (-e)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void encode(FILE *in, FILE *out) {&lt;br /&gt;
    /* encoder_state lagrar historiken av de senaste K-1 (6) bitarna. */&lt;br /&gt;
    uint8_t encoder_state = 0;&lt;br /&gt;
&lt;br /&gt;
    /* out_byte används som en tillfällig buffer för att samla ihop bitar till en hel byte. */&lt;br /&gt;
    uint8_t out_byte = 0;&lt;br /&gt;
&lt;br /&gt;
    /* bit_count håller koll på hur många bitar som för tillfället ligger i utmatningsbufferten. */&lt;br /&gt;
    int bit_count = 0;&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    /* Loopa igenom infilen tecken för tecken (byte för byte). */&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
&lt;br /&gt;
        /* Gå igenom varje enskild bit i den inlästa byten, från MSB (bit 7) till LSB (bit 0). */&lt;br /&gt;
        for (int i = 7; i &amp;gt;= 0; i--) {&lt;br /&gt;
&lt;br /&gt;
            /* Isolera biten på position i. */&lt;br /&gt;
            uint8_t bit = (ch &amp;gt;&amp;gt; i) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            /* Skapa det nuvarande ordet genom att skifta upp det gamla tillståndet till vänster&lt;br /&gt;
               och lägga till den nya inkommande biten som den lägsta biten (LSB). */&lt;br /&gt;
            uint8_t current_word = (encoder_state &amp;lt;&amp;lt; 1) | bit;&lt;br /&gt;
&lt;br /&gt;
            /* Beräkna de två paritetsbitarna (out0 och out1) genom att applicera polynommaskerna&lt;br /&gt;
               och köra XOR (paritet) på resultatet. */&lt;br /&gt;
            uint8_t out0 = parity(current_word &amp;amp; POLY_G0);&lt;br /&gt;
            uint8_t out1 = parity(current_word &amp;amp; POLY_G1);&lt;br /&gt;
&lt;br /&gt;
            /* Knuffa in den första paritetsbiten (out0) i utmatningsbyten. */&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out0;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
&lt;br /&gt;
            /* Om vi har fyllt en hel byte (8 bitar), skriv ut den till utmatningsströmmen. */&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Knuffa in den andra paritetsbiten (out1) i utmatningsbyten. */&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out1;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
&lt;br /&gt;
            /* Kontrollera återigen om utmatningsbyten blev full efter den andra biten. */&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Uppdatera kodarens interna tillstånd inför nästa bit.&lt;br /&gt;
               Vi sparar endast de lägsta K-1 bitarna med hjälp av en bitmask (63 = 0x3F). */&lt;br /&gt;
            encoder_state = current_word &amp;amp; (NUM_STATES - 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Om det finns kvarvarande bitar i bufferten när filen slutar,&lt;br /&gt;
       skiftar vi dem till vänster så att de ligger rätt, och skriver ut den sista ofullständiga byten. */&lt;br /&gt;
    if (bit_count &amp;gt; 0) {&lt;br /&gt;
        out_byte &amp;lt;&amp;lt;= (8 - bit_count);&lt;br /&gt;
        fputc(out_byte, out);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// AVKODARE (-d)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void decode(FILE *in, FILE *out) {&lt;br /&gt;
    /* Startkapacitet för historikmatrisen (antalet bit-steg). Den växer linjärt vid behov. */&lt;br /&gt;
    size_t history_capacity = 1024;&lt;br /&gt;
    size_t step = 0;&lt;br /&gt;
&lt;br /&gt;
    /* Allokera historikmatrisen dynamiskt. Det är en 2D-array av storleken [kapacitet][64].&lt;br /&gt;
       Den sparar överlevnadsbesluten (0 eller 1) för varje tillstånd vid varje givet tidssteg. */&lt;br /&gt;
    uint8_t (*history)[NUM_STATES] = malloc(history_capacity * sizeof(*history));&lt;br /&gt;
    if (!history) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* path_metrics lagrar den ackumulerade felkostnaden (Hammingavståndet) för de 64 nuvarande tillstånden. */&lt;br /&gt;
    uint32_t path_metrics[NUM_STATES];&lt;br /&gt;
&lt;br /&gt;
    /* old_metrics lagrar en oförändrad kopia av kostnaderna från föregående tidssteg. */&lt;br /&gt;
    uint32_t old_metrics[NUM_STATES];&lt;br /&gt;
&lt;br /&gt;
    /* Initiera alla tillstånd till en mycket hög felkostnad (&amp;quot;oändlighet&amp;quot;).&lt;br /&gt;
       Undantaget är tillstånd 0, vilket är det kända och garanterade starttillståndet (kostnad 0). */&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        path_metrics[i] = 1e6;&lt;br /&gt;
    }&lt;br /&gt;
    path_metrics[0] = 0;&lt;br /&gt;
&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 1: Framåtpass (Forward Pass / Add-Compare-Select)&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Läs den kodade strömmen byte för byte. */&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
&lt;br /&gt;
        /* Varje inläst byte innehåller 4 bitpar (totalt 8 kodade bitar), vilket motsvarar 4 tidssteg. */&lt;br /&gt;
        for (int bit_pair = 3; bit_pair &amp;gt;= 0; bit_pair--) {&lt;br /&gt;
&lt;br /&gt;
            /* Extrahera de två bitarna (b0 och b1) som hör till det aktuella steget. */&lt;br /&gt;
            uint8_t b0 = (ch &amp;gt;&amp;gt; (bit_pair * 2 + 1)) &amp;amp; 1;&lt;br /&gt;
            uint8_t b1 = (ch &amp;gt;&amp;gt; (bit_pair * 2)) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            /* Om vår historikmatris är full, fördubblar vi dess storlek dynamiskt med realloc.&lt;br /&gt;
               Detta gör att programmet kan hantera obegränsat stora filer och pipes. */&lt;br /&gt;
            if (step &amp;gt;= history_capacity) {&lt;br /&gt;
                history_capacity *= 2;&lt;br /&gt;
                uint8_t (*new_history)[NUM_STATES] = realloc(history, history_capacity * sizeof(*history));&lt;br /&gt;
                if (!new_history) {&lt;br /&gt;
                    free(history);&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
                history = new_history;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Kopiera över nuvarande path_metrics till old_metrics inför beräkningen av nästa steg. */&lt;br /&gt;
            memcpy(old_metrics, path_metrics, sizeof(path_metrics));&lt;br /&gt;
&lt;br /&gt;
            /* Loopa igenom alla 64 möjliga måltillstånd (nästa tillstånd) i trellis-diagrammet. */&lt;br /&gt;
            for (int state = 0; state &amp;lt; NUM_STATES; state++) {&lt;br /&gt;
&lt;br /&gt;
                /* Eftersom vi skiftar till vänster, kan vi räkna ut vilka två föregående tillstånd&lt;br /&gt;
                   (prev0 och prev1) som kan ha skiftat in i det nuvarande tillståndet. */&lt;br /&gt;
                int prev0 = state &amp;gt;&amp;gt; 1;&lt;br /&gt;
                int prev1 = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
&lt;br /&gt;
                /* Den inbit som krävdes för att nå &#039;state&#039; är identisk med nuvarande tillstånds LSB. */&lt;br /&gt;
                uint8_t input_bit = state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
                /* --- Beräkna förväntade bitar och Hammingavstånd för VÄG 0 (från prev0) --- */&lt;br /&gt;
                uint8_t word0 = (prev0 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp0_0 = parity(word0 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp0_1 = parity(word0 &amp;amp; POLY_G1);&lt;br /&gt;
                /* Kostnaden ökar med 1 för varje bit som inte matchar de faktiskt mottagna bitarna. */&lt;br /&gt;
                uint32_t cost0 = (exp0_0 != b0) + (exp0_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                /* --- Beräkna förväntade bitar och Hammingavstånd för VÄG 1 (från prev1) --- */&lt;br /&gt;
                uint8_t word1 = (prev1 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp1_0 = parity(word1 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp1_1 = parity(word1 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost1 = (exp1_0 != b0) + (exp1_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                /* Addera det gamla ackumulerade felet med det nya övergångsfelet för båda vägarna. */&lt;br /&gt;
                uint32_t m0 = old_metrics[prev0] + cost0;&lt;br /&gt;
                uint32_t m1 = old_metrics[prev1] + cost1;&lt;br /&gt;
&lt;br /&gt;
                /* (Compare &amp;amp; Select): Välj den väg som har lägst totalt fel.&lt;br /&gt;
                   Spara det nya lägsta felet i path_metrics, och dokumentera valet (0 eller 1)&lt;br /&gt;
                   i historikmatrisen för att kunna återskapa vägen baklänges senare. */&lt;br /&gt;
                if (m0 &amp;lt;= m1) {&lt;br /&gt;
                    path_metrics[state] = m0;&lt;br /&gt;
                    history[step][state] = 0;&lt;br /&gt;
                } else {&lt;br /&gt;
                    path_metrics[state] = m1;&lt;br /&gt;
                    history[step][state] = 1;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            /* Öka räknaren för antal avklarade bit-steg. */&lt;br /&gt;
            step++;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Om vi inte lyckades läsa några bitar alls, städa upp och avsluta. */&lt;br /&gt;
    if (step == 0) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 2: Hitta bästa sluttillstånd&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Sök igenom alla 64 tillstånd vid slutet av strömmen för att hitta det tillstånd&lt;br /&gt;
       som har samlat på sig minst antal fel (lägst metric). */&lt;br /&gt;
    int curr_state = 0;&lt;br /&gt;
    uint32_t min_m = path_metrics[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        if (path_metrics[i] &amp;lt; min_m) {&lt;br /&gt;
            min_m = path_metrics[i];&lt;br /&gt;
            curr_state = i;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 3: Bakåtspårning (Traceback)&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Allokera en temporär array för att spara de avkodade bitarna.&lt;br /&gt;
       Eftersom vi vandrar baklänges kommer vi att fylla denna array bakifrån. */&lt;br /&gt;
    uint8_t *decoded_bits = malloc(step);&lt;br /&gt;
    if (!decoded_bits) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Vandra baklänges genom historiken från det sista tidssteget ner till noll. */&lt;br /&gt;
    for (int s = (int)step - 1; s &amp;gt;= 0; s--) {&lt;br /&gt;
&lt;br /&gt;
        /* Den ursprungliga inbiten är alltid LSB (bit 0) i det tillstånd vi står i. */&lt;br /&gt;
        decoded_bits[s] = curr_state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
        /* Hämta beslutet (0 eller 1) som fattades för detta tillstånd i tidssteg s. */&lt;br /&gt;
        int decision = history[s][curr_state];&lt;br /&gt;
        int prev0 = curr_state &amp;gt;&amp;gt; 1;&lt;br /&gt;
&lt;br /&gt;
        /* Rekonstruera vilket tillstånd vi kom ifrån baserat på det sparade beslutet. */&lt;br /&gt;
        if (decision == 0) {&lt;br /&gt;
            curr_state = prev0;&lt;br /&gt;
        } else {&lt;br /&gt;
            curr_state = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 4: Återpacka till bytes och skriv ut&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Bitarna ligger nu i korrekt kronologisk ordning i decoded_bits-arrayen.&lt;br /&gt;
       Vi loopar igenom dem och packar ihop dem 8 och 8 till vanliga bytes igen. */&lt;br /&gt;
    uint8_t current_byte = 0;&lt;br /&gt;
    for (size_t i = 0; i &amp;lt; step; i++) {&lt;br /&gt;
        current_byte = (current_byte &amp;lt;&amp;lt; 1) | decoded_bits[i];&lt;br /&gt;
&lt;br /&gt;
        /* Varje gång vi har samlat ihop 8 bitar skriver vi ut byten till utfilen/stdout. */&lt;br /&gt;
        if ((i + 1) % 8 == 0) {&lt;br /&gt;
            fputc(current_byte, out);&lt;br /&gt;
            current_byte = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Frigör det dynamiskt allokerade minnet. */&lt;br /&gt;
    free(decoded_bits);&lt;br /&gt;
    free(history);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// INTERNT TEST (-t OCH --error)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void run_test(bool inject_errors) {&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        printf(&amp;quot;[*] Startar internt test med FELKORRIGERING (64 bytes slumpdata + 3 bitpar flippade)...\n&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
        printf(&amp;quot;[*] Startar internt test (64 bytes randomiserat data)...\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Skapa en array med 64 bytes slumpmässigt data. */&lt;br /&gt;
    uint8_t original_data[64];&lt;br /&gt;
    srand((unsigned int)time(NULL));&lt;br /&gt;
    for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
        original_data[i] = rand() % 256;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* tmpfile() skapar temporära filer som körs i RAM/hårddisk och raderas automatiskt vid stängning. */&lt;br /&gt;
    FILE *orig_stream = tmpfile();&lt;br /&gt;
    FILE *encoded_stream = tmpfile();&lt;br /&gt;
    FILE *corrupted_stream = tmpfile();&lt;br /&gt;
    FILE *decoded_stream = tmpfile();&lt;br /&gt;
&lt;br /&gt;
    if (!orig_stream || !encoded_stream || !corrupted_stream || !decoded_stream) {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Kunde inte skapa temporära filer.\n&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Skriv originaldata till startströmmen och spola tillbaka pekaren till början. */&lt;br /&gt;
    fwrite(original_data, 1, 64, orig_stream);&lt;br /&gt;
    rewind(orig_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 1. Kör kodningen (64 bytes rådata blir exakt 128 bytes packad bitström). */&lt;br /&gt;
    encode(orig_stream, encoded_stream);&lt;br /&gt;
    rewind(encoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 2. Hantering av felinjicering (--error). */&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        uint8_t encoded_data[128];&lt;br /&gt;
&lt;br /&gt;
        /* Läs in hela den kodade strömmen till en lokal array. */&lt;br /&gt;
        if (fread(encoded_data, 1, 128, encoded_stream) == 128) {&lt;br /&gt;
&lt;br /&gt;
            /* Välj ut 3 slumpmässiga bytes och flippa en slumpmässig bit i var och en av dem. */&lt;br /&gt;
            for (int i = 0; i &amp;lt; 3; i++) {&lt;br /&gt;
                int byte_index = rand() % 128;&lt;br /&gt;
                int bit_index = rand() % 8;&lt;br /&gt;
&lt;br /&gt;
                /* XOR-operatör (^) flippar den valda biten (0 blir 1, 1 blir 0). */&lt;br /&gt;
                encoded_data[byte_index] ^= (1 &amp;lt;&amp;lt; bit_index);&lt;br /&gt;
                printf(&amp;quot;[i] Introducerade bitfel i kodad byte %d, bit %d\n&amp;quot;, byte_index, bit_index);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        /* Skriv ut det saboterade datat till den korrupta strömmen. */&lt;br /&gt;
        fwrite(encoded_data, 1, 128, corrupted_stream);&lt;br /&gt;
        rewind(corrupted_stream);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Om inga fel ska testas, pekar vi bara vidare till den felfria strömmen direkt. */&lt;br /&gt;
        corrupted_stream = encoded_stream;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* 3. Kör avkodaren på dataströmmen. */&lt;br /&gt;
    decode(corrupted_stream, decoded_stream);&lt;br /&gt;
    rewind(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 4. Läs tillbaka det färdigavkodade datat och verifiera resultatet. */&lt;br /&gt;
    uint8_t final_data[64];&lt;br /&gt;
    size_t read_bytes = fread(final_data, 1, 64, decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    bool success = true;&lt;br /&gt;
    if (read_bytes != 64) {&lt;br /&gt;
        printf(&amp;quot;[FAIL] Avkodade fel antal bytes: %zu (förväntade 64)\n&amp;quot;, read_bytes);&lt;br /&gt;
        success = false;&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Jämför ursprunglig array med den avkodade arrayen byte för byte. */&lt;br /&gt;
        for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
            if (original_data[i] != final_data[i]) {&lt;br /&gt;
                printf(&amp;quot;[FAIL] Diff vid byte %d: Org=0x%02X, Avkodad=0x%02X\n&amp;quot;, i, original_data[i], final_data[i]);&lt;br /&gt;
                success = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Stäng alla strömmar ordentligt. */&lt;br /&gt;
    fclose(orig_stream);&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        fclose(encoded_stream);&lt;br /&gt;
        fclose(corrupted_stream);&lt;br /&gt;
    } else {&lt;br /&gt;
        fclose(encoded_stream);&lt;br /&gt;
    }&lt;br /&gt;
    fclose(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* Skriv ut slutrapport för testet. */&lt;br /&gt;
    if (success) {&lt;br /&gt;
        if (inject_errors) {&lt;br /&gt;
            printf(&amp;quot;[SUCCESS] Viterbi-algoritmens felkorrigering lyckades! Alla bitfel lagades och datat matchar perfekt.\n&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
            printf(&amp;quot;[SUCCESS] Testet lyckades! Allt data matchar perfekt.\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// HJÄLPTEXT OCH MAIN&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void print_help(const char *prog_name) {&lt;br /&gt;
    printf(&amp;quot;Användning: %s [option]\n&amp;quot;, prog_name);&lt;br /&gt;
    printf(&amp;quot;Optioner:\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -e &amp;lt;infil &amp;gt;utfil   Kodar infil till utfil (Rate 1/2, K=7)\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -d &amp;lt;infil &amp;gt;utfil   Avkodar infil till utfil med Viterbi\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -h                 Visar denna hjälptext\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -t                 Kör internt test med 64 bytes slumpdata\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  --error            Kör internt test men injicerar bitfel för att testa felkorrigeringen\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    /* Säkerställ att användaren har skickat med exakt ett kommandoradsargument. */&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Kontrollera vilket argument användaren skickade och anropa rätt funktion. */&lt;br /&gt;
    if (strcmp(argv[1], &amp;quot;-e&amp;quot;) == 0) {&lt;br /&gt;
        encode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-d&amp;quot;) == 0) {&lt;br /&gt;
        decode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-h&amp;quot;) == 0) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-t&amp;quot;) == 0) {&lt;br /&gt;
        run_test(false);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;--error&amp;quot;) == 0) {&lt;br /&gt;
        run_test(true);&lt;br /&gt;
    } else {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Fel: Okänd parameter &#039;%s&#039;\n&amp;quot;, argv[1]);&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1912</id>
		<title>Viterbi</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1912"/>
		<updated>2026-06-23T18:17:35Z</updated>

		<summary type="html">&lt;p&gt;Anders: /* Bakgrund */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:algoritmer]]&lt;br /&gt;
[[category:felkorrigering]]&lt;br /&gt;
[[category:telekom]]&lt;br /&gt;
&lt;br /&gt;
= Bakgrund = &lt;br /&gt;
&lt;br /&gt;
== Viterbis algoritm – Hur vi hittar rätt signal i bruset ==&lt;br /&gt;
&lt;br /&gt;
Inom amatörradion och digital signalbehandling stöter vi ofta på problemet med brus och fading. När vi skickar digital data över etern, till exempel med modernare digitala trafiksätt eller vid satellitkommunikation, använder vi ofta något som kallas för faltningskodning (convolutional coding). Det är en metod där varje sänd bit beror på både den nuvarande biten och ett antal tidigare bitar. Detta skapar ett mönster, ett beroende, som gör signalen robust.&lt;br /&gt;
&lt;br /&gt;
Men hur avkodar vi detta på mottagarsidan när atmosfären har ställt till det och lagt till en massa brus? Det är här Viterbis algoritm kommer in i bilden.&lt;br /&gt;
&lt;br /&gt;
Kort sagt är Viterbialgoritmen en metod för att hitta den mest troliga sekvensen av dolda tillstånd (det vi faktiskt sände) baserat på en serie observerade händelser (den brusiga signalen vi tog emot). Istället för att gissa bit för bit, tittar algoritmen på hela sammanhanget.&lt;br /&gt;
&lt;br /&gt;
== Trellis-diagrammet: Vägarna genom historien ==&lt;br /&gt;
&lt;br /&gt;
För att förstå hur algoritmen jobbar kan vi tänka oss ett nätverk av vägar, ett så kallat trellis-diagram.&lt;br /&gt;
&lt;br /&gt;
* Tillstånd (States): Radiosändarens &amp;quot;minne&amp;quot; kan vid varje given tidpunkt befinna sig i ett visst antal tillstånd (beroende på de senaste sända bitarna).&lt;br /&gt;
&lt;br /&gt;
* Övergångar: När en ny bit ska sändas hoppar sändaren till ett nytt tillstånd och skickar ut en specifik kombination av bitar i luften.&lt;br /&gt;
&lt;br /&gt;
== Hur funkar det? ==&lt;br /&gt;
&lt;br /&gt;
När mottagaren lyssnar får den in en sekvens som kanske är halvt dränkt i brus. En nolla kanske ser ut som en svag etta, och så vidare. Viterbialgoritmen börjar nu räkna på alla möjliga vägar genom detta nätverk av tillstånd över tid.&lt;br /&gt;
Så här jobbar algoritmen steg för steg:&lt;br /&gt;
&lt;br /&gt;
# Beräkna avstånd (Metric): För varje steg i tiden tittar algoritmen på de bitar som faktiskt togs emot och jämför dem med vad som borde ha sänts för varje tänkbar väg i diagrammet. Man mäter &amp;quot;avståndet&amp;quot; (ofta Hamming-avstånd för ettor och nollor, eller Euklidiskt avstånd om man kör &amp;quot;soft decision&amp;quot; där man mäter signalstyrkan mer exakt). Ju mindre avstånd, desto mer troligt är det att den vägen är den rätta.&lt;br /&gt;
# Spara den bästa vägen (Add-Compare-Select): Till varje nytt tillstånd i diagrammet leder det oftast två olika vägar från det föregående steget. Algoritmen adderar det nya avståndet till det gamla, jämför de två alternativen, och väljer den väg som har lägst totalt avstånd (fel). Den andra vägen slängs bort för alltid. Det här är algoritmens stora styrka: den rensar hela tiden bort återvändsgränder så att beräkningsbördan inte exploderar.&lt;br /&gt;
# Lås historiken (Traceback): När hela sekvensen (eller ett tillräckligt långt block) har tagits emot, tittar man på vilket slutgiltigt tillstånd som har det absolut lägsta totala felvärdet. Sedan backar man bakåt genom de sparade &amp;quot;bästa vägarna&amp;quot; till början.&lt;br /&gt;
&lt;br /&gt;
Resultatet blir den absolut mest sannolika sekvensen av bitar som sändaren faktiskt skickade ut, trots att det var fullt av bitfel under resans gång.&lt;br /&gt;
&lt;br /&gt;
= Implementation i hyggligt modern C=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Anders Sikvall 2026 Version 1.0&lt;br /&gt;
 * &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
 * &lt;br /&gt;
 * Implementation i C av en standard Viterbi-algoritm som används bl.a. av &lt;br /&gt;
 * Qualcomm och NASA och är välpublicerad.&lt;br /&gt;
 *&lt;br /&gt;
 * PUBLIC DOMAIN FOR EDUCATIONAL PURPOSES&lt;br /&gt;
 *&lt;br /&gt;
 * Kompilera med gcc -o viterbi viterbi.c&lt;br /&gt;
 *&lt;br /&gt;
 * ANVÄNDNING&lt;br /&gt;
 *         viterbi -e &amp;lt;indatastream &amp;gt;utdatastream   Kodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -d &amp;lt;indatastream &amp;gt;utdatastream   Avkodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -t                               Utför en intern test på 64 randomiserade oktetter&lt;br /&gt;
 *         viterbi -h                               Visar en kort hjälptext&lt;br /&gt;
 *         viterbi --error                          Samma sak som -t men introducerar 3 flippade bitpar&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Constraint length (K=7) innebär att skiftregistret minns de senaste 7 bitarna. */&lt;br /&gt;
#define K 7&lt;br /&gt;
&lt;br /&gt;
/* Antalet tillstånd i trellis-diagrammet bestäms av de senaste K-1 bitarna.&lt;br /&gt;
   2^(7-1) = 2^6 = 64 unika tillstånd totalt. */&lt;br /&gt;
#define NUM_STATES (1 &amp;lt;&amp;lt; (K - 1))&lt;br /&gt;
&lt;br /&gt;
/* Standard NASA/Qualcomm-polynom för Rate 1/2 konvolutionskodning.&lt;br /&gt;
   POLY_G0 motsvarar oktalt 151 (1001111 binärt).&lt;br /&gt;
   POLY_G1 motsvarar oktalt 113 (1101101 binärt).&lt;br /&gt;
   Dessa maskar används för att bestämma vilka bitar i tillståndet som XOR-sammanfogas. */&lt;br /&gt;
const uint8_t POLY_G0 = 0x4F;&lt;br /&gt;
const uint8_t POLY_G1 = 0x6D;&lt;br /&gt;
&lt;br /&gt;
/* Hårdvaruoptimerad paritetsberäkning.&lt;br /&gt;
   Räknar antalet satta bitar (1:or) i ett tal och returnerar 1 om antalet är udda,&lt;br /&gt;
   eller 0 om antalet är jämnt (motsvarar en kedja av XOR-grindar). */&lt;br /&gt;
static inline uint8_t parity(uint8_t x) {&lt;br /&gt;
    return __builtin_parity(x) &amp;amp; 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// KODARE (-e)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void encode(FILE *in, FILE *out) {&lt;br /&gt;
    /* encoder_state lagrar historiken av de senaste K-1 (6) bitarna. */&lt;br /&gt;
    uint8_t encoder_state = 0;&lt;br /&gt;
&lt;br /&gt;
    /* out_byte används som en tillfällig buffer för att samla ihop bitar till en hel byte. */&lt;br /&gt;
    uint8_t out_byte = 0;&lt;br /&gt;
&lt;br /&gt;
    /* bit_count håller koll på hur många bitar som för tillfället ligger i utmatningsbufferten. */&lt;br /&gt;
    int bit_count = 0;&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    /* Loopa igenom infilen tecken för tecken (byte för byte). */&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
&lt;br /&gt;
        /* Gå igenom varje enskild bit i den inlästa byten, från MSB (bit 7) till LSB (bit 0). */&lt;br /&gt;
        for (int i = 7; i &amp;gt;= 0; i--) {&lt;br /&gt;
&lt;br /&gt;
            /* Isolera biten på position i. */&lt;br /&gt;
            uint8_t bit = (ch &amp;gt;&amp;gt; i) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            /* Skapa det nuvarande ordet genom att skifta upp det gamla tillståndet till vänster&lt;br /&gt;
               och lägga till den nya inkommande biten som den lägsta biten (LSB). */&lt;br /&gt;
            uint8_t current_word = (encoder_state &amp;lt;&amp;lt; 1) | bit;&lt;br /&gt;
&lt;br /&gt;
            /* Beräkna de två paritetsbitarna (out0 och out1) genom att applicera polynommaskerna&lt;br /&gt;
               och köra XOR (paritet) på resultatet. */&lt;br /&gt;
            uint8_t out0 = parity(current_word &amp;amp; POLY_G0);&lt;br /&gt;
            uint8_t out1 = parity(current_word &amp;amp; POLY_G1);&lt;br /&gt;
&lt;br /&gt;
            /* Knuffa in den första paritetsbiten (out0) i utmatningsbyten. */&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out0;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
&lt;br /&gt;
            /* Om vi har fyllt en hel byte (8 bitar), skriv ut den till utmatningsströmmen. */&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Knuffa in den andra paritetsbiten (out1) i utmatningsbyten. */&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out1;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
&lt;br /&gt;
            /* Kontrollera återigen om utmatningsbyten blev full efter den andra biten. */&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Uppdatera kodarens interna tillstånd inför nästa bit.&lt;br /&gt;
               Vi sparar endast de lägsta K-1 bitarna med hjälp av en bitmask (63 = 0x3F). */&lt;br /&gt;
            encoder_state = current_word &amp;amp; (NUM_STATES - 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Om det finns kvarvarande bitar i bufferten när filen slutar,&lt;br /&gt;
       skiftar vi dem till vänster så att de ligger rätt, och skriver ut den sista ofullständiga byten. */&lt;br /&gt;
    if (bit_count &amp;gt; 0) {&lt;br /&gt;
        out_byte &amp;lt;&amp;lt;= (8 - bit_count);&lt;br /&gt;
        fputc(out_byte, out);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// AVKODARE (-d)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void decode(FILE *in, FILE *out) {&lt;br /&gt;
    /* Startkapacitet för historikmatrisen (antalet bit-steg). Den växer linjärt vid behov. */&lt;br /&gt;
    size_t history_capacity = 1024;&lt;br /&gt;
    size_t step = 0;&lt;br /&gt;
&lt;br /&gt;
    /* Allokera historikmatrisen dynamiskt. Det är en 2D-array av storleken [kapacitet][64].&lt;br /&gt;
       Den sparar överlevnadsbesluten (0 eller 1) för varje tillstånd vid varje givet tidssteg. */&lt;br /&gt;
    uint8_t (*history)[NUM_STATES] = malloc(history_capacity * sizeof(*history));&lt;br /&gt;
    if (!history) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* path_metrics lagrar den ackumulerade felkostnaden (Hammingavståndet) för de 64 nuvarande tillstånden. */&lt;br /&gt;
    uint32_t path_metrics[NUM_STATES];&lt;br /&gt;
&lt;br /&gt;
    /* old_metrics lagrar en oförändrad kopia av kostnaderna från föregående tidssteg. */&lt;br /&gt;
    uint32_t old_metrics[NUM_STATES];&lt;br /&gt;
&lt;br /&gt;
    /* Initiera alla tillstånd till en mycket hög felkostnad (&amp;quot;oändlighet&amp;quot;).&lt;br /&gt;
       Undantaget är tillstånd 0, vilket är det kända och garanterade starttillståndet (kostnad 0). */&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        path_metrics[i] = 1e6;&lt;br /&gt;
    }&lt;br /&gt;
    path_metrics[0] = 0;&lt;br /&gt;
&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 1: Framåtpass (Forward Pass / Add-Compare-Select)&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Läs den kodade strömmen byte för byte. */&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
&lt;br /&gt;
        /* Varje inläst byte innehåller 4 bitpar (totalt 8 kodade bitar), vilket motsvarar 4 tidssteg. */&lt;br /&gt;
        for (int bit_pair = 3; bit_pair &amp;gt;= 0; bit_pair--) {&lt;br /&gt;
&lt;br /&gt;
            /* Extrahera de två bitarna (b0 och b1) som hör till det aktuella steget. */&lt;br /&gt;
            uint8_t b0 = (ch &amp;gt;&amp;gt; (bit_pair * 2 + 1)) &amp;amp; 1;&lt;br /&gt;
            uint8_t b1 = (ch &amp;gt;&amp;gt; (bit_pair * 2)) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            /* Om vår historikmatris är full, fördubblar vi dess storlek dynamiskt med realloc.&lt;br /&gt;
               Detta gör att programmet kan hantera obegränsat stora filer och pipes. */&lt;br /&gt;
            if (step &amp;gt;= history_capacity) {&lt;br /&gt;
                history_capacity *= 2;&lt;br /&gt;
                uint8_t (*new_history)[NUM_STATES] = realloc(history, history_capacity * sizeof(*history));&lt;br /&gt;
                if (!new_history) {&lt;br /&gt;
                    free(history);&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
                history = new_history;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Kopiera över nuvarande path_metrics till old_metrics inför beräkningen av nästa steg. */&lt;br /&gt;
            memcpy(old_metrics, path_metrics, sizeof(path_metrics));&lt;br /&gt;
&lt;br /&gt;
            /* Loopa igenom alla 64 möjliga måltillstånd (nästa tillstånd) i trellis-diagrammet. */&lt;br /&gt;
            for (int state = 0; state &amp;lt; NUM_STATES; state++) {&lt;br /&gt;
&lt;br /&gt;
                /* Eftersom vi skiftar till vänster, kan vi räkna ut vilka två föregående tillstånd&lt;br /&gt;
                   (prev0 och prev1) som kan ha skiftat in i det nuvarande tillståndet. */&lt;br /&gt;
                int prev0 = state &amp;gt;&amp;gt; 1;&lt;br /&gt;
                int prev1 = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
&lt;br /&gt;
                /* Den inbit som krävdes för att nå &#039;state&#039; är identisk med nuvarande tillstånds LSB. */&lt;br /&gt;
                uint8_t input_bit = state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
                /* --- Beräkna förväntade bitar och Hammingavstånd för VÄG 0 (från prev0) --- */&lt;br /&gt;
                uint8_t word0 = (prev0 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp0_0 = parity(word0 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp0_1 = parity(word0 &amp;amp; POLY_G1);&lt;br /&gt;
                /* Kostnaden ökar med 1 för varje bit som inte matchar de faktiskt mottagna bitarna. */&lt;br /&gt;
                uint32_t cost0 = (exp0_0 != b0) + (exp0_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                /* --- Beräkna förväntade bitar och Hammingavstånd för VÄG 1 (från prev1) --- */&lt;br /&gt;
                uint8_t word1 = (prev1 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp1_0 = parity(word1 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp1_1 = parity(word1 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost1 = (exp1_0 != b0) + (exp1_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                /* Addera det gamla ackumulerade felet med det nya övergångsfelet för båda vägarna. */&lt;br /&gt;
                uint32_t m0 = old_metrics[prev0] + cost0;&lt;br /&gt;
                uint32_t m1 = old_metrics[prev1] + cost1;&lt;br /&gt;
&lt;br /&gt;
                /* (Compare &amp;amp; Select): Välj den väg som har lägst totalt fel.&lt;br /&gt;
                   Spara det nya lägsta felet i path_metrics, och dokumentera valet (0 eller 1)&lt;br /&gt;
                   i historikmatrisen för att kunna återskapa vägen baklänges senare. */&lt;br /&gt;
                if (m0 &amp;lt;= m1) {&lt;br /&gt;
                    path_metrics[state] = m0;&lt;br /&gt;
                    history[step][state] = 0;&lt;br /&gt;
                } else {&lt;br /&gt;
                    path_metrics[state] = m1;&lt;br /&gt;
                    history[step][state] = 1;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            /* Öka räknaren för antal avklarade bit-steg. */&lt;br /&gt;
            step++;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Om vi inte lyckades läsa några bitar alls, städa upp och avsluta. */&lt;br /&gt;
    if (step == 0) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 2: Hitta bästa sluttillstånd&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Sök igenom alla 64 tillstånd vid slutet av strömmen för att hitta det tillstånd&lt;br /&gt;
       som har samlat på sig minst antal fel (lägst metric). */&lt;br /&gt;
    int curr_state = 0;&lt;br /&gt;
    uint32_t min_m = path_metrics[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        if (path_metrics[i] &amp;lt; min_m) {&lt;br /&gt;
            min_m = path_metrics[i];&lt;br /&gt;
            curr_state = i;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 3: Bakåtspårning (Traceback)&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Allokera en temporär array för att spara de avkodade bitarna.&lt;br /&gt;
       Eftersom vi vandrar baklänges kommer vi att fylla denna array bakifrån. */&lt;br /&gt;
    uint8_t *decoded_bits = malloc(step);&lt;br /&gt;
    if (!decoded_bits) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Vandra baklänges genom historiken från det sista tidssteget ner till noll. */&lt;br /&gt;
    for (int s = (int)step - 1; s &amp;gt;= 0; s--) {&lt;br /&gt;
&lt;br /&gt;
        /* Den ursprungliga inbiten är alltid LSB (bit 0) i det tillstånd vi står i. */&lt;br /&gt;
        decoded_bits[s] = curr_state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
        /* Hämta beslutet (0 eller 1) som fattades för detta tillstånd i tidssteg s. */&lt;br /&gt;
        int decision = history[s][curr_state];&lt;br /&gt;
        int prev0 = curr_state &amp;gt;&amp;gt; 1;&lt;br /&gt;
&lt;br /&gt;
        /* Rekonstruera vilket tillstånd vi kom ifrån baserat på det sparade beslutet. */&lt;br /&gt;
        if (decision == 0) {&lt;br /&gt;
            curr_state = prev0;&lt;br /&gt;
        } else {&lt;br /&gt;
            curr_state = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 4: Återpacka till bytes och skriv ut&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Bitarna ligger nu i korrekt kronologisk ordning i decoded_bits-arrayen.&lt;br /&gt;
       Vi loopar igenom dem och packar ihop dem 8 och 8 till vanliga bytes igen. */&lt;br /&gt;
    uint8_t current_byte = 0;&lt;br /&gt;
    for (size_t i = 0; i &amp;lt; step; i++) {&lt;br /&gt;
        current_byte = (current_byte &amp;lt;&amp;lt; 1) | decoded_bits[i];&lt;br /&gt;
&lt;br /&gt;
        /* Varje gång vi har samlat ihop 8 bitar skriver vi ut byten till utfilen/stdout. */&lt;br /&gt;
        if ((i + 1) % 8 == 0) {&lt;br /&gt;
            fputc(current_byte, out);&lt;br /&gt;
            current_byte = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Frigör det dynamiskt allokerade minnet. */&lt;br /&gt;
    free(decoded_bits);&lt;br /&gt;
    free(history);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// INTERNT TEST (-t OCH --error)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void run_test(bool inject_errors) {&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        printf(&amp;quot;[*] Startar internt test med FELKORRIGERING (64 bytes slumpdata + 3 bitpar flippade)...\n&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
        printf(&amp;quot;[*] Startar internt test (64 bytes randomiserat data)...\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Skapa en array med 64 bytes slumpmässigt data. */&lt;br /&gt;
    uint8_t original_data[64];&lt;br /&gt;
    srand((unsigned int)time(NULL));&lt;br /&gt;
    for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
        original_data[i] = rand() % 256;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* tmpfile() skapar temporära filer som körs i RAM/hårddisk och raderas automatiskt vid stängning. */&lt;br /&gt;
    FILE *orig_stream = tmpfile();&lt;br /&gt;
    FILE *encoded_stream = tmpfile();&lt;br /&gt;
    FILE *corrupted_stream = tmpfile();&lt;br /&gt;
    FILE *decoded_stream = tmpfile();&lt;br /&gt;
&lt;br /&gt;
    if (!orig_stream || !encoded_stream || !corrupted_stream || !decoded_stream) {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Kunde inte skapa temporära filer.\n&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Skriv originaldata till startströmmen och spola tillbaka pekaren till början. */&lt;br /&gt;
    fwrite(original_data, 1, 64, orig_stream);&lt;br /&gt;
    rewind(orig_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 1. Kör kodningen (64 bytes rådata blir exakt 128 bytes packad bitström). */&lt;br /&gt;
    encode(orig_stream, encoded_stream);&lt;br /&gt;
    rewind(encoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 2. Hantering av felinjicering (--error). */&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        uint8_t encoded_data[128];&lt;br /&gt;
&lt;br /&gt;
        /* Läs in hela den kodade strömmen till en lokal array. */&lt;br /&gt;
        if (fread(encoded_data, 1, 128, encoded_stream) == 128) {&lt;br /&gt;
&lt;br /&gt;
            /* Välj ut 3 slumpmässiga bytes och flippa en slumpmässig bit i var och en av dem. */&lt;br /&gt;
            for (int i = 0; i &amp;lt; 3; i++) {&lt;br /&gt;
                int byte_index = rand() % 128;&lt;br /&gt;
                int bit_index = rand() % 8;&lt;br /&gt;
&lt;br /&gt;
                /* XOR-operatör (^) flippar den valda biten (0 blir 1, 1 blir 0). */&lt;br /&gt;
                encoded_data[byte_index] ^= (1 &amp;lt;&amp;lt; bit_index);&lt;br /&gt;
                printf(&amp;quot;[i] Introducerade bitfel i kodad byte %d, bit %d\n&amp;quot;, byte_index, bit_index);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        /* Skriv ut det saboterade datat till den korrupta strömmen. */&lt;br /&gt;
        fwrite(encoded_data, 1, 128, corrupted_stream);&lt;br /&gt;
        rewind(corrupted_stream);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Om inga fel ska testas, pekar vi bara vidare till den felfria strömmen direkt. */&lt;br /&gt;
        corrupted_stream = encoded_stream;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* 3. Kör avkodaren på dataströmmen. */&lt;br /&gt;
    decode(corrupted_stream, decoded_stream);&lt;br /&gt;
    rewind(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 4. Läs tillbaka det färdigavkodade datat och verifiera resultatet. */&lt;br /&gt;
    uint8_t final_data[64];&lt;br /&gt;
    size_t read_bytes = fread(final_data, 1, 64, decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    bool success = true;&lt;br /&gt;
    if (read_bytes != 64) {&lt;br /&gt;
        printf(&amp;quot;[FAIL] Avkodade fel antal bytes: %zu (förväntade 64)\n&amp;quot;, read_bytes);&lt;br /&gt;
        success = false;&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Jämför ursprunglig array med den avkodade arrayen byte för byte. */&lt;br /&gt;
        for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
            if (original_data[i] != final_data[i]) {&lt;br /&gt;
                printf(&amp;quot;[FAIL] Diff vid byte %d: Org=0x%02X, Avkodad=0x%02X\n&amp;quot;, i, original_data[i], final_data[i]);&lt;br /&gt;
                success = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Stäng alla strömmar ordentligt. */&lt;br /&gt;
    fclose(orig_stream);&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        fclose(encoded_stream);&lt;br /&gt;
        fclose(corrupted_stream);&lt;br /&gt;
    } else {&lt;br /&gt;
        fclose(encoded_stream);&lt;br /&gt;
    }&lt;br /&gt;
    fclose(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* Skriv ut slutrapport för testet. */&lt;br /&gt;
    if (success) {&lt;br /&gt;
        if (inject_errors) {&lt;br /&gt;
            printf(&amp;quot;[SUCCESS] Viterbi-algoritmens felkorrigering lyckades! Alla bitfel lagades och datat matchar perfekt.\n&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
            printf(&amp;quot;[SUCCESS] Testet lyckades! Allt data matchar perfekt.\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// HJÄLPTEXT OCH MAIN&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void print_help(const char *prog_name) {&lt;br /&gt;
    printf(&amp;quot;Användning: %s [option]\n&amp;quot;, prog_name);&lt;br /&gt;
    printf(&amp;quot;Optioner:\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -e &amp;lt;infil &amp;gt;utfil   Kodar infil till utfil (Rate 1/2, K=7)\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -d &amp;lt;infil &amp;gt;utfil   Avkodar infil till utfil med Viterbi\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -h                 Visar denna hjälptext\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -t                 Kör internt test med 64 bytes slumpdata\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  --error            Kör internt test men injicerar bitfel för att testa felkorrigeringen\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    /* Säkerställ att användaren har skickat med exakt ett kommandoradsargument. */&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Kontrollera vilket argument användaren skickade och anropa rätt funktion. */&lt;br /&gt;
    if (strcmp(argv[1], &amp;quot;-e&amp;quot;) == 0) {&lt;br /&gt;
        encode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-d&amp;quot;) == 0) {&lt;br /&gt;
        decode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-h&amp;quot;) == 0) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-t&amp;quot;) == 0) {&lt;br /&gt;
        run_test(false);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;--error&amp;quot;) == 0) {&lt;br /&gt;
        run_test(true);&lt;br /&gt;
    } else {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Fel: Okänd parameter &#039;%s&#039;\n&amp;quot;, argv[1]);&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1911</id>
		<title>Viterbi</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1911"/>
		<updated>2026-06-23T17:58:49Z</updated>

		<summary type="html">&lt;p&gt;Anders: /* Implementation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Bakgrund = &lt;br /&gt;
&lt;br /&gt;
== Viterbis algoritm – Hur vi hittar rätt signal i bruset ==&lt;br /&gt;
&lt;br /&gt;
Inom amatörradion och digital signalbehandling stöter vi ofta på problemet med brus och fading. När vi skickar digital data över etern, till exempel med modernare digitala trafiksätt eller vid satellitkommunikation, använder vi ofta något som kallas för faltningskodning (convolutional coding). Det är en metod där varje sänd bit beror på både den nuvarande biten och ett antal tidigare bitar. Detta skapar ett mönster, ett beroende, som gör signalen robust.&lt;br /&gt;
&lt;br /&gt;
Men hur avkodar vi detta på mottagarsidan när atmosfären har ställt till det och lagt till en massa brus? Det är här Viterbis algoritm kommer in i bilden.&lt;br /&gt;
&lt;br /&gt;
Kort sagt är Viterbialgoritmen en metod för att hitta den mest troliga sekvensen av dolda tillstånd (det vi faktiskt sände) baserat på en serie observerade händelser (den brusiga signalen vi tog emot). Istället för att gissa bit för bit, tittar algoritmen på hela sammanhanget.&lt;br /&gt;
&lt;br /&gt;
== Trellis-diagrammet: Vägarna genom historien ==&lt;br /&gt;
&lt;br /&gt;
För att förstå hur algoritmen jobbar kan vi tänka oss ett nätverk av vägar, ett så kallat trellis-diagram.&lt;br /&gt;
&lt;br /&gt;
* Tillstånd (States): Radiosändarens &amp;quot;minne&amp;quot; kan vid varje given tidpunkt befinna sig i ett visst antal tillstånd (beroende på de senaste sända bitarna).&lt;br /&gt;
&lt;br /&gt;
* Övergångar: När en ny bit ska sändas hoppar sändaren till ett nytt tillstånd och skickar ut en specifik kombination av bitar i luften.&lt;br /&gt;
&lt;br /&gt;
== Hur funkar det? ==&lt;br /&gt;
&lt;br /&gt;
När mottagaren lyssnar får den in en sekvens som kanske är halvt dränkt i brus. En nolla kanske ser ut som en svag etta, och så vidare. Viterbialgoritmen börjar nu räkna på alla möjliga vägar genom detta nätverk av tillstånd över tid.&lt;br /&gt;
Så här jobbar algoritmen steg för steg:&lt;br /&gt;
&lt;br /&gt;
# Beräkna avstånd (Metric): För varje steg i tiden tittar algoritmen på de bitar som faktiskt togs emot och jämför dem med vad som borde ha sänts för varje tänkbar väg i diagrammet. Man mäter &amp;quot;avståndet&amp;quot; (ofta Hamming-avstånd för ettor och nollor, eller Euklidiskt avstånd om man kör &amp;quot;soft decision&amp;quot; där man mäter signalstyrkan mer exakt). Ju mindre avstånd, desto mer troligt är det att den vägen är den rätta.&lt;br /&gt;
# Spara den bästa vägen (Add-Compare-Select): Till varje nytt tillstånd i diagrammet leder det oftast två olika vägar från det föregående steget. Algoritmen adderar det nya avståndet till det gamla, jämför de två alternativen, och väljer den väg som har lägst totalt avstånd (fel). Den andra vägen slängs bort för alltid. Det här är algoritmens stora styrka: den rensar hela tiden bort återvändsgränder så att beräkningsbördan inte exploderar.&lt;br /&gt;
# Lås historiken (Traceback): När hela sekvensen (eller ett tillräckligt långt block) har tagits emot, tittar man på vilket slutgiltigt tillstånd som har det absolut lägsta totala felvärdet. Sedan backar man bakåt genom de sparade &amp;quot;bästa vägarna&amp;quot; till början.&lt;br /&gt;
&lt;br /&gt;
Resultatet blir den absolut mest sannolika sekvensen av bitar som sändaren faktiskt skickade ut, trots att det var fullt av bitfel under resans gång.&lt;br /&gt;
&lt;br /&gt;
= Implementation i hyggligt modern C=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Anders Sikvall 2026 Version 1.0&lt;br /&gt;
 * &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
 * &lt;br /&gt;
 * Implementation i C av en standard Viterbi-algoritm som används bl.a. av &lt;br /&gt;
 * Qualcomm och NASA och är välpublicerad.&lt;br /&gt;
 *&lt;br /&gt;
 * PUBLIC DOMAIN FOR EDUCATIONAL PURPOSES&lt;br /&gt;
 *&lt;br /&gt;
 * Kompilera med gcc -o viterbi viterbi.c&lt;br /&gt;
 *&lt;br /&gt;
 * ANVÄNDNING&lt;br /&gt;
 *         viterbi -e &amp;lt;indatastream &amp;gt;utdatastream   Kodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -d &amp;lt;indatastream &amp;gt;utdatastream   Avkodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -t                               Utför en intern test på 64 randomiserade oktetter&lt;br /&gt;
 *         viterbi -h                               Visar en kort hjälptext&lt;br /&gt;
 *         viterbi --error                          Samma sak som -t men introducerar 3 flippade bitpar&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Constraint length (K=7) innebär att skiftregistret minns de senaste 7 bitarna. */&lt;br /&gt;
#define K 7&lt;br /&gt;
&lt;br /&gt;
/* Antalet tillstånd i trellis-diagrammet bestäms av de senaste K-1 bitarna.&lt;br /&gt;
   2^(7-1) = 2^6 = 64 unika tillstånd totalt. */&lt;br /&gt;
#define NUM_STATES (1 &amp;lt;&amp;lt; (K - 1))&lt;br /&gt;
&lt;br /&gt;
/* Standard NASA/Qualcomm-polynom för Rate 1/2 konvolutionskodning.&lt;br /&gt;
   POLY_G0 motsvarar oktalt 151 (1001111 binärt).&lt;br /&gt;
   POLY_G1 motsvarar oktalt 113 (1101101 binärt).&lt;br /&gt;
   Dessa maskar används för att bestämma vilka bitar i tillståndet som XOR-sammanfogas. */&lt;br /&gt;
const uint8_t POLY_G0 = 0x4F;&lt;br /&gt;
const uint8_t POLY_G1 = 0x6D;&lt;br /&gt;
&lt;br /&gt;
/* Hårdvaruoptimerad paritetsberäkning.&lt;br /&gt;
   Räknar antalet satta bitar (1:or) i ett tal och returnerar 1 om antalet är udda,&lt;br /&gt;
   eller 0 om antalet är jämnt (motsvarar en kedja av XOR-grindar). */&lt;br /&gt;
static inline uint8_t parity(uint8_t x) {&lt;br /&gt;
    return __builtin_parity(x) &amp;amp; 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// KODARE (-e)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void encode(FILE *in, FILE *out) {&lt;br /&gt;
    /* encoder_state lagrar historiken av de senaste K-1 (6) bitarna. */&lt;br /&gt;
    uint8_t encoder_state = 0;&lt;br /&gt;
&lt;br /&gt;
    /* out_byte används som en tillfällig buffer för att samla ihop bitar till en hel byte. */&lt;br /&gt;
    uint8_t out_byte = 0;&lt;br /&gt;
&lt;br /&gt;
    /* bit_count håller koll på hur många bitar som för tillfället ligger i utmatningsbufferten. */&lt;br /&gt;
    int bit_count = 0;&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    /* Loopa igenom infilen tecken för tecken (byte för byte). */&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
&lt;br /&gt;
        /* Gå igenom varje enskild bit i den inlästa byten, från MSB (bit 7) till LSB (bit 0). */&lt;br /&gt;
        for (int i = 7; i &amp;gt;= 0; i--) {&lt;br /&gt;
&lt;br /&gt;
            /* Isolera biten på position i. */&lt;br /&gt;
            uint8_t bit = (ch &amp;gt;&amp;gt; i) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            /* Skapa det nuvarande ordet genom att skifta upp det gamla tillståndet till vänster&lt;br /&gt;
               och lägga till den nya inkommande biten som den lägsta biten (LSB). */&lt;br /&gt;
            uint8_t current_word = (encoder_state &amp;lt;&amp;lt; 1) | bit;&lt;br /&gt;
&lt;br /&gt;
            /* Beräkna de två paritetsbitarna (out0 och out1) genom att applicera polynommaskerna&lt;br /&gt;
               och köra XOR (paritet) på resultatet. */&lt;br /&gt;
            uint8_t out0 = parity(current_word &amp;amp; POLY_G0);&lt;br /&gt;
            uint8_t out1 = parity(current_word &amp;amp; POLY_G1);&lt;br /&gt;
&lt;br /&gt;
            /* Knuffa in den första paritetsbiten (out0) i utmatningsbyten. */&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out0;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
&lt;br /&gt;
            /* Om vi har fyllt en hel byte (8 bitar), skriv ut den till utmatningsströmmen. */&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Knuffa in den andra paritetsbiten (out1) i utmatningsbyten. */&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out1;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
&lt;br /&gt;
            /* Kontrollera återigen om utmatningsbyten blev full efter den andra biten. */&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Uppdatera kodarens interna tillstånd inför nästa bit.&lt;br /&gt;
               Vi sparar endast de lägsta K-1 bitarna med hjälp av en bitmask (63 = 0x3F). */&lt;br /&gt;
            encoder_state = current_word &amp;amp; (NUM_STATES - 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Om det finns kvarvarande bitar i bufferten när filen slutar,&lt;br /&gt;
       skiftar vi dem till vänster så att de ligger rätt, och skriver ut den sista ofullständiga byten. */&lt;br /&gt;
    if (bit_count &amp;gt; 0) {&lt;br /&gt;
        out_byte &amp;lt;&amp;lt;= (8 - bit_count);&lt;br /&gt;
        fputc(out_byte, out);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// AVKODARE (-d)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void decode(FILE *in, FILE *out) {&lt;br /&gt;
    /* Startkapacitet för historikmatrisen (antalet bit-steg). Den växer linjärt vid behov. */&lt;br /&gt;
    size_t history_capacity = 1024;&lt;br /&gt;
    size_t step = 0;&lt;br /&gt;
&lt;br /&gt;
    /* Allokera historikmatrisen dynamiskt. Det är en 2D-array av storleken [kapacitet][64].&lt;br /&gt;
       Den sparar överlevnadsbesluten (0 eller 1) för varje tillstånd vid varje givet tidssteg. */&lt;br /&gt;
    uint8_t (*history)[NUM_STATES] = malloc(history_capacity * sizeof(*history));&lt;br /&gt;
    if (!history) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* path_metrics lagrar den ackumulerade felkostnaden (Hammingavståndet) för de 64 nuvarande tillstånden. */&lt;br /&gt;
    uint32_t path_metrics[NUM_STATES];&lt;br /&gt;
&lt;br /&gt;
    /* old_metrics lagrar en oförändrad kopia av kostnaderna från föregående tidssteg. */&lt;br /&gt;
    uint32_t old_metrics[NUM_STATES];&lt;br /&gt;
&lt;br /&gt;
    /* Initiera alla tillstånd till en mycket hög felkostnad (&amp;quot;oändlighet&amp;quot;).&lt;br /&gt;
       Undantaget är tillstånd 0, vilket är det kända och garanterade starttillståndet (kostnad 0). */&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        path_metrics[i] = 1e6;&lt;br /&gt;
    }&lt;br /&gt;
    path_metrics[0] = 0;&lt;br /&gt;
&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 1: Framåtpass (Forward Pass / Add-Compare-Select)&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Läs den kodade strömmen byte för byte. */&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
&lt;br /&gt;
        /* Varje inläst byte innehåller 4 bitpar (totalt 8 kodade bitar), vilket motsvarar 4 tidssteg. */&lt;br /&gt;
        for (int bit_pair = 3; bit_pair &amp;gt;= 0; bit_pair--) {&lt;br /&gt;
&lt;br /&gt;
            /* Extrahera de två bitarna (b0 och b1) som hör till det aktuella steget. */&lt;br /&gt;
            uint8_t b0 = (ch &amp;gt;&amp;gt; (bit_pair * 2 + 1)) &amp;amp; 1;&lt;br /&gt;
            uint8_t b1 = (ch &amp;gt;&amp;gt; (bit_pair * 2)) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            /* Om vår historikmatris är full, fördubblar vi dess storlek dynamiskt med realloc.&lt;br /&gt;
               Detta gör att programmet kan hantera obegränsat stora filer och pipes. */&lt;br /&gt;
            if (step &amp;gt;= history_capacity) {&lt;br /&gt;
                history_capacity *= 2;&lt;br /&gt;
                uint8_t (*new_history)[NUM_STATES] = realloc(history, history_capacity * sizeof(*history));&lt;br /&gt;
                if (!new_history) {&lt;br /&gt;
                    free(history);&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
                history = new_history;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Kopiera över nuvarande path_metrics till old_metrics inför beräkningen av nästa steg. */&lt;br /&gt;
            memcpy(old_metrics, path_metrics, sizeof(path_metrics));&lt;br /&gt;
&lt;br /&gt;
            /* Loopa igenom alla 64 möjliga måltillstånd (nästa tillstånd) i trellis-diagrammet. */&lt;br /&gt;
            for (int state = 0; state &amp;lt; NUM_STATES; state++) {&lt;br /&gt;
&lt;br /&gt;
                /* Eftersom vi skiftar till vänster, kan vi räkna ut vilka två föregående tillstånd&lt;br /&gt;
                   (prev0 och prev1) som kan ha skiftat in i det nuvarande tillståndet. */&lt;br /&gt;
                int prev0 = state &amp;gt;&amp;gt; 1;&lt;br /&gt;
                int prev1 = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
&lt;br /&gt;
                /* Den inbit som krävdes för att nå &#039;state&#039; är identisk med nuvarande tillstånds LSB. */&lt;br /&gt;
                uint8_t input_bit = state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
                /* --- Beräkna förväntade bitar och Hammingavstånd för VÄG 0 (från prev0) --- */&lt;br /&gt;
                uint8_t word0 = (prev0 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp0_0 = parity(word0 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp0_1 = parity(word0 &amp;amp; POLY_G1);&lt;br /&gt;
                /* Kostnaden ökar med 1 för varje bit som inte matchar de faktiskt mottagna bitarna. */&lt;br /&gt;
                uint32_t cost0 = (exp0_0 != b0) + (exp0_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                /* --- Beräkna förväntade bitar och Hammingavstånd för VÄG 1 (från prev1) --- */&lt;br /&gt;
                uint8_t word1 = (prev1 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp1_0 = parity(word1 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp1_1 = parity(word1 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost1 = (exp1_0 != b0) + (exp1_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                /* Addera det gamla ackumulerade felet med det nya övergångsfelet för båda vägarna. */&lt;br /&gt;
                uint32_t m0 = old_metrics[prev0] + cost0;&lt;br /&gt;
                uint32_t m1 = old_metrics[prev1] + cost1;&lt;br /&gt;
&lt;br /&gt;
                /* (Compare &amp;amp; Select): Välj den väg som har lägst totalt fel.&lt;br /&gt;
                   Spara det nya lägsta felet i path_metrics, och dokumentera valet (0 eller 1)&lt;br /&gt;
                   i historikmatrisen för att kunna återskapa vägen baklänges senare. */&lt;br /&gt;
                if (m0 &amp;lt;= m1) {&lt;br /&gt;
                    path_metrics[state] = m0;&lt;br /&gt;
                    history[step][state] = 0;&lt;br /&gt;
                } else {&lt;br /&gt;
                    path_metrics[state] = m1;&lt;br /&gt;
                    history[step][state] = 1;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            /* Öka räknaren för antal avklarade bit-steg. */&lt;br /&gt;
            step++;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Om vi inte lyckades läsa några bitar alls, städa upp och avsluta. */&lt;br /&gt;
    if (step == 0) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 2: Hitta bästa sluttillstånd&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Sök igenom alla 64 tillstånd vid slutet av strömmen för att hitta det tillstånd&lt;br /&gt;
       som har samlat på sig minst antal fel (lägst metric). */&lt;br /&gt;
    int curr_state = 0;&lt;br /&gt;
    uint32_t min_m = path_metrics[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        if (path_metrics[i] &amp;lt; min_m) {&lt;br /&gt;
            min_m = path_metrics[i];&lt;br /&gt;
            curr_state = i;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 3: Bakåtspårning (Traceback)&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Allokera en temporär array för att spara de avkodade bitarna.&lt;br /&gt;
       Eftersom vi vandrar baklänges kommer vi att fylla denna array bakifrån. */&lt;br /&gt;
    uint8_t *decoded_bits = malloc(step);&lt;br /&gt;
    if (!decoded_bits) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Vandra baklänges genom historiken från det sista tidssteget ner till noll. */&lt;br /&gt;
    for (int s = (int)step - 1; s &amp;gt;= 0; s--) {&lt;br /&gt;
&lt;br /&gt;
        /* Den ursprungliga inbiten är alltid LSB (bit 0) i det tillstånd vi står i. */&lt;br /&gt;
        decoded_bits[s] = curr_state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
        /* Hämta beslutet (0 eller 1) som fattades för detta tillstånd i tidssteg s. */&lt;br /&gt;
        int decision = history[s][curr_state];&lt;br /&gt;
        int prev0 = curr_state &amp;gt;&amp;gt; 1;&lt;br /&gt;
&lt;br /&gt;
        /* Rekonstruera vilket tillstånd vi kom ifrån baserat på det sparade beslutet. */&lt;br /&gt;
        if (decision == 0) {&lt;br /&gt;
            curr_state = prev0;&lt;br /&gt;
        } else {&lt;br /&gt;
            curr_state = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 4: Återpacka till bytes och skriv ut&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Bitarna ligger nu i korrekt kronologisk ordning i decoded_bits-arrayen.&lt;br /&gt;
       Vi loopar igenom dem och packar ihop dem 8 och 8 till vanliga bytes igen. */&lt;br /&gt;
    uint8_t current_byte = 0;&lt;br /&gt;
    for (size_t i = 0; i &amp;lt; step; i++) {&lt;br /&gt;
        current_byte = (current_byte &amp;lt;&amp;lt; 1) | decoded_bits[i];&lt;br /&gt;
&lt;br /&gt;
        /* Varje gång vi har samlat ihop 8 bitar skriver vi ut byten till utfilen/stdout. */&lt;br /&gt;
        if ((i + 1) % 8 == 0) {&lt;br /&gt;
            fputc(current_byte, out);&lt;br /&gt;
            current_byte = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Frigör det dynamiskt allokerade minnet. */&lt;br /&gt;
    free(decoded_bits);&lt;br /&gt;
    free(history);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// INTERNT TEST (-t OCH --error)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void run_test(bool inject_errors) {&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        printf(&amp;quot;[*] Startar internt test med FELKORRIGERING (64 bytes slumpdata + 3 bitpar flippade)...\n&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
        printf(&amp;quot;[*] Startar internt test (64 bytes randomiserat data)...\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Skapa en array med 64 bytes slumpmässigt data. */&lt;br /&gt;
    uint8_t original_data[64];&lt;br /&gt;
    srand((unsigned int)time(NULL));&lt;br /&gt;
    for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
        original_data[i] = rand() % 256;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* tmpfile() skapar temporära filer som körs i RAM/hårddisk och raderas automatiskt vid stängning. */&lt;br /&gt;
    FILE *orig_stream = tmpfile();&lt;br /&gt;
    FILE *encoded_stream = tmpfile();&lt;br /&gt;
    FILE *corrupted_stream = tmpfile();&lt;br /&gt;
    FILE *decoded_stream = tmpfile();&lt;br /&gt;
&lt;br /&gt;
    if (!orig_stream || !encoded_stream || !corrupted_stream || !decoded_stream) {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Kunde inte skapa temporära filer.\n&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Skriv originaldata till startströmmen och spola tillbaka pekaren till början. */&lt;br /&gt;
    fwrite(original_data, 1, 64, orig_stream);&lt;br /&gt;
    rewind(orig_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 1. Kör kodningen (64 bytes rådata blir exakt 128 bytes packad bitström). */&lt;br /&gt;
    encode(orig_stream, encoded_stream);&lt;br /&gt;
    rewind(encoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 2. Hantering av felinjicering (--error). */&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        uint8_t encoded_data[128];&lt;br /&gt;
&lt;br /&gt;
        /* Läs in hela den kodade strömmen till en lokal array. */&lt;br /&gt;
        if (fread(encoded_data, 1, 128, encoded_stream) == 128) {&lt;br /&gt;
&lt;br /&gt;
            /* Välj ut 3 slumpmässiga bytes och flippa en slumpmässig bit i var och en av dem. */&lt;br /&gt;
            for (int i = 0; i &amp;lt; 3; i++) {&lt;br /&gt;
                int byte_index = rand() % 128;&lt;br /&gt;
                int bit_index = rand() % 8;&lt;br /&gt;
&lt;br /&gt;
                /* XOR-operatör (^) flippar den valda biten (0 blir 1, 1 blir 0). */&lt;br /&gt;
                encoded_data[byte_index] ^= (1 &amp;lt;&amp;lt; bit_index);&lt;br /&gt;
                printf(&amp;quot;[i] Introducerade bitfel i kodad byte %d, bit %d\n&amp;quot;, byte_index, bit_index);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        /* Skriv ut det saboterade datat till den korrupta strömmen. */&lt;br /&gt;
        fwrite(encoded_data, 1, 128, corrupted_stream);&lt;br /&gt;
        rewind(corrupted_stream);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Om inga fel ska testas, pekar vi bara vidare till den felfria strömmen direkt. */&lt;br /&gt;
        corrupted_stream = encoded_stream;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* 3. Kör avkodaren på dataströmmen. */&lt;br /&gt;
    decode(corrupted_stream, decoded_stream);&lt;br /&gt;
    rewind(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 4. Läs tillbaka det färdigavkodade datat och verifiera resultatet. */&lt;br /&gt;
    uint8_t final_data[64];&lt;br /&gt;
    size_t read_bytes = fread(final_data, 1, 64, decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    bool success = true;&lt;br /&gt;
    if (read_bytes != 64) {&lt;br /&gt;
        printf(&amp;quot;[FAIL] Avkodade fel antal bytes: %zu (förväntade 64)\n&amp;quot;, read_bytes);&lt;br /&gt;
        success = false;&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Jämför ursprunglig array med den avkodade arrayen byte för byte. */&lt;br /&gt;
        for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
            if (original_data[i] != final_data[i]) {&lt;br /&gt;
                printf(&amp;quot;[FAIL] Diff vid byte %d: Org=0x%02X, Avkodad=0x%02X\n&amp;quot;, i, original_data[i], final_data[i]);&lt;br /&gt;
                success = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Stäng alla strömmar ordentligt. */&lt;br /&gt;
    fclose(orig_stream);&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        fclose(encoded_stream);&lt;br /&gt;
        fclose(corrupted_stream);&lt;br /&gt;
    } else {&lt;br /&gt;
        fclose(encoded_stream);&lt;br /&gt;
    }&lt;br /&gt;
    fclose(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* Skriv ut slutrapport för testet. */&lt;br /&gt;
    if (success) {&lt;br /&gt;
        if (inject_errors) {&lt;br /&gt;
            printf(&amp;quot;[SUCCESS] Viterbi-algoritmens felkorrigering lyckades! Alla bitfel lagades och datat matchar perfekt.\n&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
            printf(&amp;quot;[SUCCESS] Testet lyckades! Allt data matchar perfekt.\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// HJÄLPTEXT OCH MAIN&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void print_help(const char *prog_name) {&lt;br /&gt;
    printf(&amp;quot;Användning: %s [option]\n&amp;quot;, prog_name);&lt;br /&gt;
    printf(&amp;quot;Optioner:\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -e &amp;lt;infil &amp;gt;utfil   Kodar infil till utfil (Rate 1/2, K=7)\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -d &amp;lt;infil &amp;gt;utfil   Avkodar infil till utfil med Viterbi\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -h                 Visar denna hjälptext\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -t                 Kör internt test med 64 bytes slumpdata\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  --error            Kör internt test men injicerar bitfel för att testa felkorrigeringen\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    /* Säkerställ att användaren har skickat med exakt ett kommandoradsargument. */&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Kontrollera vilket argument användaren skickade och anropa rätt funktion. */&lt;br /&gt;
    if (strcmp(argv[1], &amp;quot;-e&amp;quot;) == 0) {&lt;br /&gt;
        encode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-d&amp;quot;) == 0) {&lt;br /&gt;
        decode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-h&amp;quot;) == 0) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-t&amp;quot;) == 0) {&lt;br /&gt;
        run_test(false);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;--error&amp;quot;) == 0) {&lt;br /&gt;
        run_test(true);&lt;br /&gt;
    } else {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Fel: Okänd parameter &#039;%s&#039;\n&amp;quot;, argv[1]);&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1910</id>
		<title>Viterbi</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1910"/>
		<updated>2026-06-23T17:55:56Z</updated>

		<summary type="html">&lt;p&gt;Anders: /* Hur funkar det? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Bakgrund = &lt;br /&gt;
&lt;br /&gt;
== Viterbis algoritm – Hur vi hittar rätt signal i bruset ==&lt;br /&gt;
&lt;br /&gt;
Inom amatörradion och digital signalbehandling stöter vi ofta på problemet med brus och fading. När vi skickar digital data över etern, till exempel med modernare digitala trafiksätt eller vid satellitkommunikation, använder vi ofta något som kallas för faltningskodning (convolutional coding). Det är en metod där varje sänd bit beror på både den nuvarande biten och ett antal tidigare bitar. Detta skapar ett mönster, ett beroende, som gör signalen robust.&lt;br /&gt;
&lt;br /&gt;
Men hur avkodar vi detta på mottagarsidan när atmosfären har ställt till det och lagt till en massa brus? Det är här Viterbis algoritm kommer in i bilden.&lt;br /&gt;
&lt;br /&gt;
Kort sagt är Viterbialgoritmen en metod för att hitta den mest troliga sekvensen av dolda tillstånd (det vi faktiskt sände) baserat på en serie observerade händelser (den brusiga signalen vi tog emot). Istället för att gissa bit för bit, tittar algoritmen på hela sammanhanget.&lt;br /&gt;
&lt;br /&gt;
== Trellis-diagrammet: Vägarna genom historien ==&lt;br /&gt;
&lt;br /&gt;
För att förstå hur algoritmen jobbar kan vi tänka oss ett nätverk av vägar, ett så kallat trellis-diagram.&lt;br /&gt;
&lt;br /&gt;
* Tillstånd (States): Radiosändarens &amp;quot;minne&amp;quot; kan vid varje given tidpunkt befinna sig i ett visst antal tillstånd (beroende på de senaste sända bitarna).&lt;br /&gt;
&lt;br /&gt;
* Övergångar: När en ny bit ska sändas hoppar sändaren till ett nytt tillstånd och skickar ut en specifik kombination av bitar i luften.&lt;br /&gt;
&lt;br /&gt;
== Hur funkar det? ==&lt;br /&gt;
&lt;br /&gt;
När mottagaren lyssnar får den in en sekvens som kanske är halvt dränkt i brus. En nolla kanske ser ut som en svag etta, och så vidare. Viterbialgoritmen börjar nu räkna på alla möjliga vägar genom detta nätverk av tillstånd över tid.&lt;br /&gt;
Så här jobbar algoritmen steg för steg:&lt;br /&gt;
&lt;br /&gt;
# Beräkna avstånd (Metric): För varje steg i tiden tittar algoritmen på de bitar som faktiskt togs emot och jämför dem med vad som borde ha sänts för varje tänkbar väg i diagrammet. Man mäter &amp;quot;avståndet&amp;quot; (ofta Hamming-avstånd för ettor och nollor, eller Euklidiskt avstånd om man kör &amp;quot;soft decision&amp;quot; där man mäter signalstyrkan mer exakt). Ju mindre avstånd, desto mer troligt är det att den vägen är den rätta.&lt;br /&gt;
# Spara den bästa vägen (Add-Compare-Select): Till varje nytt tillstånd i diagrammet leder det oftast två olika vägar från det föregående steget. Algoritmen adderar det nya avståndet till det gamla, jämför de två alternativen, och väljer den väg som har lägst totalt avstånd (fel). Den andra vägen slängs bort för alltid. Det här är algoritmens stora styrka: den rensar hela tiden bort återvändsgränder så att beräkningsbördan inte exploderar.&lt;br /&gt;
# Lås historiken (Traceback): När hela sekvensen (eller ett tillräckligt långt block) har tagits emot, tittar man på vilket slutgiltigt tillstånd som har det absolut lägsta totala felvärdet. Sedan backar man bakåt genom de sparade &amp;quot;bästa vägarna&amp;quot; till början.&lt;br /&gt;
&lt;br /&gt;
Resultatet blir den absolut mest sannolika sekvensen av bitar som sändaren faktiskt skickade ut, trots att det var fullt av bitfel under resans gång.&lt;br /&gt;
&lt;br /&gt;
= Implementation =&lt;br /&gt;
&lt;br /&gt;
== Modern C ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Anders Sikvall 2026 Version 1.0&lt;br /&gt;
 * &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
 * &lt;br /&gt;
 * Implementation i C av en standard Viterbi-algoritm som används bl.a. av &lt;br /&gt;
 * Qualcomm och NASA och är välpublicerad.&lt;br /&gt;
 *&lt;br /&gt;
 * PUBLIC DOMAIN FOR EDUCATIONAL PURPOSES&lt;br /&gt;
 *&lt;br /&gt;
 * Kompilera med gcc -o viterbi viterbi.c&lt;br /&gt;
 *&lt;br /&gt;
 * ANVÄNDNING&lt;br /&gt;
 *         viterbi -e &amp;lt;indatastream &amp;gt;utdatastream   Kodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -d &amp;lt;indatastream &amp;gt;utdatastream   Avkodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -t                               Utför en intern test på 64 randomiserade oktetter&lt;br /&gt;
 *         viterbi -h                               Visar en kort hjälptext&lt;br /&gt;
 *         viterbi --error                          Samma sak som -t men introducerar 3 flippade bitpar&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Constraint length (K=7) innebär att skiftregistret minns de senaste 7 bitarna. */&lt;br /&gt;
#define K 7&lt;br /&gt;
&lt;br /&gt;
/* Antalet tillstånd i trellis-diagrammet bestäms av de senaste K-1 bitarna.&lt;br /&gt;
   2^(7-1) = 2^6 = 64 unika tillstånd totalt. */&lt;br /&gt;
#define NUM_STATES (1 &amp;lt;&amp;lt; (K - 1))&lt;br /&gt;
&lt;br /&gt;
/* Standard NASA/Qualcomm-polynom för Rate 1/2 konvolutionskodning.&lt;br /&gt;
   POLY_G0 motsvarar oktalt 151 (1001111 binärt).&lt;br /&gt;
   POLY_G1 motsvarar oktalt 113 (1101101 binärt).&lt;br /&gt;
   Dessa maskar används för att bestämma vilka bitar i tillståndet som XOR-sammanfogas. */&lt;br /&gt;
const uint8_t POLY_G0 = 0x4F;&lt;br /&gt;
const uint8_t POLY_G1 = 0x6D;&lt;br /&gt;
&lt;br /&gt;
/* Hårdvaruoptimerad paritetsberäkning.&lt;br /&gt;
   Räknar antalet satta bitar (1:or) i ett tal och returnerar 1 om antalet är udda,&lt;br /&gt;
   eller 0 om antalet är jämnt (motsvarar en kedja av XOR-grindar). */&lt;br /&gt;
static inline uint8_t parity(uint8_t x) {&lt;br /&gt;
    return __builtin_parity(x) &amp;amp; 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// KODARE (-e)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void encode(FILE *in, FILE *out) {&lt;br /&gt;
    /* encoder_state lagrar historiken av de senaste K-1 (6) bitarna. */&lt;br /&gt;
    uint8_t encoder_state = 0;&lt;br /&gt;
&lt;br /&gt;
    /* out_byte används som en tillfällig buffer för att samla ihop bitar till en hel byte. */&lt;br /&gt;
    uint8_t out_byte = 0;&lt;br /&gt;
&lt;br /&gt;
    /* bit_count håller koll på hur många bitar som för tillfället ligger i utmatningsbufferten. */&lt;br /&gt;
    int bit_count = 0;&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    /* Loopa igenom infilen tecken för tecken (byte för byte). */&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
&lt;br /&gt;
        /* Gå igenom varje enskild bit i den inlästa byten, från MSB (bit 7) till LSB (bit 0). */&lt;br /&gt;
        for (int i = 7; i &amp;gt;= 0; i--) {&lt;br /&gt;
&lt;br /&gt;
            /* Isolera biten på position i. */&lt;br /&gt;
            uint8_t bit = (ch &amp;gt;&amp;gt; i) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            /* Skapa det nuvarande ordet genom att skifta upp det gamla tillståndet till vänster&lt;br /&gt;
               och lägga till den nya inkommande biten som den lägsta biten (LSB). */&lt;br /&gt;
            uint8_t current_word = (encoder_state &amp;lt;&amp;lt; 1) | bit;&lt;br /&gt;
&lt;br /&gt;
            /* Beräkna de två paritetsbitarna (out0 och out1) genom att applicera polynommaskerna&lt;br /&gt;
               och köra XOR (paritet) på resultatet. */&lt;br /&gt;
            uint8_t out0 = parity(current_word &amp;amp; POLY_G0);&lt;br /&gt;
            uint8_t out1 = parity(current_word &amp;amp; POLY_G1);&lt;br /&gt;
&lt;br /&gt;
            /* Knuffa in den första paritetsbiten (out0) i utmatningsbyten. */&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out0;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
&lt;br /&gt;
            /* Om vi har fyllt en hel byte (8 bitar), skriv ut den till utmatningsströmmen. */&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Knuffa in den andra paritetsbiten (out1) i utmatningsbyten. */&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out1;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
&lt;br /&gt;
            /* Kontrollera återigen om utmatningsbyten blev full efter den andra biten. */&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Uppdatera kodarens interna tillstånd inför nästa bit.&lt;br /&gt;
               Vi sparar endast de lägsta K-1 bitarna med hjälp av en bitmask (63 = 0x3F). */&lt;br /&gt;
            encoder_state = current_word &amp;amp; (NUM_STATES - 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Om det finns kvarvarande bitar i bufferten när filen slutar,&lt;br /&gt;
       skiftar vi dem till vänster så att de ligger rätt, och skriver ut den sista ofullständiga byten. */&lt;br /&gt;
    if (bit_count &amp;gt; 0) {&lt;br /&gt;
        out_byte &amp;lt;&amp;lt;= (8 - bit_count);&lt;br /&gt;
        fputc(out_byte, out);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// AVKODARE (-d)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void decode(FILE *in, FILE *out) {&lt;br /&gt;
    /* Startkapacitet för historikmatrisen (antalet bit-steg). Den växer linjärt vid behov. */&lt;br /&gt;
    size_t history_capacity = 1024;&lt;br /&gt;
    size_t step = 0;&lt;br /&gt;
&lt;br /&gt;
    /* Allokera historikmatrisen dynamiskt. Det är en 2D-array av storleken [kapacitet][64].&lt;br /&gt;
       Den sparar överlevnadsbesluten (0 eller 1) för varje tillstånd vid varje givet tidssteg. */&lt;br /&gt;
    uint8_t (*history)[NUM_STATES] = malloc(history_capacity * sizeof(*history));&lt;br /&gt;
    if (!history) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* path_metrics lagrar den ackumulerade felkostnaden (Hammingavståndet) för de 64 nuvarande tillstånden. */&lt;br /&gt;
    uint32_t path_metrics[NUM_STATES];&lt;br /&gt;
&lt;br /&gt;
    /* old_metrics lagrar en oförändrad kopia av kostnaderna från föregående tidssteg. */&lt;br /&gt;
    uint32_t old_metrics[NUM_STATES];&lt;br /&gt;
&lt;br /&gt;
    /* Initiera alla tillstånd till en mycket hög felkostnad (&amp;quot;oändlighet&amp;quot;).&lt;br /&gt;
       Undantaget är tillstånd 0, vilket är det kända och garanterade starttillståndet (kostnad 0). */&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        path_metrics[i] = 1e6;&lt;br /&gt;
    }&lt;br /&gt;
    path_metrics[0] = 0;&lt;br /&gt;
&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 1: Framåtpass (Forward Pass / Add-Compare-Select)&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Läs den kodade strömmen byte för byte. */&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
&lt;br /&gt;
        /* Varje inläst byte innehåller 4 bitpar (totalt 8 kodade bitar), vilket motsvarar 4 tidssteg. */&lt;br /&gt;
        for (int bit_pair = 3; bit_pair &amp;gt;= 0; bit_pair--) {&lt;br /&gt;
&lt;br /&gt;
            /* Extrahera de två bitarna (b0 och b1) som hör till det aktuella steget. */&lt;br /&gt;
            uint8_t b0 = (ch &amp;gt;&amp;gt; (bit_pair * 2 + 1)) &amp;amp; 1;&lt;br /&gt;
            uint8_t b1 = (ch &amp;gt;&amp;gt; (bit_pair * 2)) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            /* Om vår historikmatris är full, fördubblar vi dess storlek dynamiskt med realloc.&lt;br /&gt;
               Detta gör att programmet kan hantera obegränsat stora filer och pipes. */&lt;br /&gt;
            if (step &amp;gt;= history_capacity) {&lt;br /&gt;
                history_capacity *= 2;&lt;br /&gt;
                uint8_t (*new_history)[NUM_STATES] = realloc(history, history_capacity * sizeof(*history));&lt;br /&gt;
                if (!new_history) {&lt;br /&gt;
                    free(history);&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
                history = new_history;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Kopiera över nuvarande path_metrics till old_metrics inför beräkningen av nästa steg. */&lt;br /&gt;
            memcpy(old_metrics, path_metrics, sizeof(path_metrics));&lt;br /&gt;
&lt;br /&gt;
            /* Loopa igenom alla 64 möjliga måltillstånd (nästa tillstånd) i trellis-diagrammet. */&lt;br /&gt;
            for (int state = 0; state &amp;lt; NUM_STATES; state++) {&lt;br /&gt;
&lt;br /&gt;
                /* Eftersom vi skiftar till vänster, kan vi räkna ut vilka två föregående tillstånd&lt;br /&gt;
                   (prev0 och prev1) som kan ha skiftat in i det nuvarande tillståndet. */&lt;br /&gt;
                int prev0 = state &amp;gt;&amp;gt; 1;&lt;br /&gt;
                int prev1 = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
&lt;br /&gt;
                /* Den inbit som krävdes för att nå &#039;state&#039; är identisk med nuvarande tillstånds LSB. */&lt;br /&gt;
                uint8_t input_bit = state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
                /* --- Beräkna förväntade bitar och Hammingavstånd för VÄG 0 (från prev0) --- */&lt;br /&gt;
                uint8_t word0 = (prev0 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp0_0 = parity(word0 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp0_1 = parity(word0 &amp;amp; POLY_G1);&lt;br /&gt;
                /* Kostnaden ökar med 1 för varje bit som inte matchar de faktiskt mottagna bitarna. */&lt;br /&gt;
                uint32_t cost0 = (exp0_0 != b0) + (exp0_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                /* --- Beräkna förväntade bitar och Hammingavstånd för VÄG 1 (från prev1) --- */&lt;br /&gt;
                uint8_t word1 = (prev1 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp1_0 = parity(word1 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp1_1 = parity(word1 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost1 = (exp1_0 != b0) + (exp1_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                /* Addera det gamla ackumulerade felet med det nya övergångsfelet för båda vägarna. */&lt;br /&gt;
                uint32_t m0 = old_metrics[prev0] + cost0;&lt;br /&gt;
                uint32_t m1 = old_metrics[prev1] + cost1;&lt;br /&gt;
&lt;br /&gt;
                /* (Compare &amp;amp; Select): Välj den väg som har lägst totalt fel.&lt;br /&gt;
                   Spara det nya lägsta felet i path_metrics, och dokumentera valet (0 eller 1)&lt;br /&gt;
                   i historikmatrisen för att kunna återskapa vägen baklänges senare. */&lt;br /&gt;
                if (m0 &amp;lt;= m1) {&lt;br /&gt;
                    path_metrics[state] = m0;&lt;br /&gt;
                    history[step][state] = 0;&lt;br /&gt;
                } else {&lt;br /&gt;
                    path_metrics[state] = m1;&lt;br /&gt;
                    history[step][state] = 1;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            /* Öka räknaren för antal avklarade bit-steg. */&lt;br /&gt;
            step++;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Om vi inte lyckades läsa några bitar alls, städa upp och avsluta. */&lt;br /&gt;
    if (step == 0) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 2: Hitta bästa sluttillstånd&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Sök igenom alla 64 tillstånd vid slutet av strömmen för att hitta det tillstånd&lt;br /&gt;
       som har samlat på sig minst antal fel (lägst metric). */&lt;br /&gt;
    int curr_state = 0;&lt;br /&gt;
    uint32_t min_m = path_metrics[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        if (path_metrics[i] &amp;lt; min_m) {&lt;br /&gt;
            min_m = path_metrics[i];&lt;br /&gt;
            curr_state = i;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 3: Bakåtspårning (Traceback)&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Allokera en temporär array för att spara de avkodade bitarna.&lt;br /&gt;
       Eftersom vi vandrar baklänges kommer vi att fylla denna array bakifrån. */&lt;br /&gt;
    uint8_t *decoded_bits = malloc(step);&lt;br /&gt;
    if (!decoded_bits) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Vandra baklänges genom historiken från det sista tidssteget ner till noll. */&lt;br /&gt;
    for (int s = (int)step - 1; s &amp;gt;= 0; s--) {&lt;br /&gt;
&lt;br /&gt;
        /* Den ursprungliga inbiten är alltid LSB (bit 0) i det tillstånd vi står i. */&lt;br /&gt;
        decoded_bits[s] = curr_state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
        /* Hämta beslutet (0 eller 1) som fattades för detta tillstånd i tidssteg s. */&lt;br /&gt;
        int decision = history[s][curr_state];&lt;br /&gt;
        int prev0 = curr_state &amp;gt;&amp;gt; 1;&lt;br /&gt;
&lt;br /&gt;
        /* Rekonstruera vilket tillstånd vi kom ifrån baserat på det sparade beslutet. */&lt;br /&gt;
        if (decision == 0) {&lt;br /&gt;
            curr_state = prev0;&lt;br /&gt;
        } else {&lt;br /&gt;
            curr_state = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 4: Återpacka till bytes och skriv ut&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Bitarna ligger nu i korrekt kronologisk ordning i decoded_bits-arrayen.&lt;br /&gt;
       Vi loopar igenom dem och packar ihop dem 8 och 8 till vanliga bytes igen. */&lt;br /&gt;
    uint8_t current_byte = 0;&lt;br /&gt;
    for (size_t i = 0; i &amp;lt; step; i++) {&lt;br /&gt;
        current_byte = (current_byte &amp;lt;&amp;lt; 1) | decoded_bits[i];&lt;br /&gt;
&lt;br /&gt;
        /* Varje gång vi har samlat ihop 8 bitar skriver vi ut byten till utfilen/stdout. */&lt;br /&gt;
        if ((i + 1) % 8 == 0) {&lt;br /&gt;
            fputc(current_byte, out);&lt;br /&gt;
            current_byte = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Frigör det dynamiskt allokerade minnet. */&lt;br /&gt;
    free(decoded_bits);&lt;br /&gt;
    free(history);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// INTERNT TEST (-t OCH --error)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void run_test(bool inject_errors) {&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        printf(&amp;quot;[*] Startar internt test med FELKORRIGERING (64 bytes slumpdata + 3 bitpar flippade)...\n&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
        printf(&amp;quot;[*] Startar internt test (64 bytes randomiserat data)...\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Skapa en array med 64 bytes slumpmässigt data. */&lt;br /&gt;
    uint8_t original_data[64];&lt;br /&gt;
    srand((unsigned int)time(NULL));&lt;br /&gt;
    for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
        original_data[i] = rand() % 256;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* tmpfile() skapar temporära filer som körs i RAM/hårddisk och raderas automatiskt vid stängning. */&lt;br /&gt;
    FILE *orig_stream = tmpfile();&lt;br /&gt;
    FILE *encoded_stream = tmpfile();&lt;br /&gt;
    FILE *corrupted_stream = tmpfile();&lt;br /&gt;
    FILE *decoded_stream = tmpfile();&lt;br /&gt;
&lt;br /&gt;
    if (!orig_stream || !encoded_stream || !corrupted_stream || !decoded_stream) {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Kunde inte skapa temporära filer.\n&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Skriv originaldata till startströmmen och spola tillbaka pekaren till början. */&lt;br /&gt;
    fwrite(original_data, 1, 64, orig_stream);&lt;br /&gt;
    rewind(orig_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 1. Kör kodningen (64 bytes rådata blir exakt 128 bytes packad bitström). */&lt;br /&gt;
    encode(orig_stream, encoded_stream);&lt;br /&gt;
    rewind(encoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 2. Hantering av felinjicering (--error). */&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        uint8_t encoded_data[128];&lt;br /&gt;
&lt;br /&gt;
        /* Läs in hela den kodade strömmen till en lokal array. */&lt;br /&gt;
        if (fread(encoded_data, 1, 128, encoded_stream) == 128) {&lt;br /&gt;
&lt;br /&gt;
            /* Välj ut 3 slumpmässiga bytes och flippa en slumpmässig bit i var och en av dem. */&lt;br /&gt;
            for (int i = 0; i &amp;lt; 3; i++) {&lt;br /&gt;
                int byte_index = rand() % 128;&lt;br /&gt;
                int bit_index = rand() % 8;&lt;br /&gt;
&lt;br /&gt;
                /* XOR-operatör (^) flippar den valda biten (0 blir 1, 1 blir 0). */&lt;br /&gt;
                encoded_data[byte_index] ^= (1 &amp;lt;&amp;lt; bit_index);&lt;br /&gt;
                printf(&amp;quot;[i] Introducerade bitfel i kodad byte %d, bit %d\n&amp;quot;, byte_index, bit_index);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        /* Skriv ut det saboterade datat till den korrupta strömmen. */&lt;br /&gt;
        fwrite(encoded_data, 1, 128, corrupted_stream);&lt;br /&gt;
        rewind(corrupted_stream);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Om inga fel ska testas, pekar vi bara vidare till den felfria strömmen direkt. */&lt;br /&gt;
        corrupted_stream = encoded_stream;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* 3. Kör avkodaren på dataströmmen. */&lt;br /&gt;
    decode(corrupted_stream, decoded_stream);&lt;br /&gt;
    rewind(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 4. Läs tillbaka det färdigavkodade datat och verifiera resultatet. */&lt;br /&gt;
    uint8_t final_data[64];&lt;br /&gt;
    size_t read_bytes = fread(final_data, 1, 64, decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    bool success = true;&lt;br /&gt;
    if (read_bytes != 64) {&lt;br /&gt;
        printf(&amp;quot;[FAIL] Avkodade fel antal bytes: %zu (förväntade 64)\n&amp;quot;, read_bytes);&lt;br /&gt;
        success = false;&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Jämför ursprunglig array med den avkodade arrayen byte för byte. */&lt;br /&gt;
        for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
            if (original_data[i] != final_data[i]) {&lt;br /&gt;
                printf(&amp;quot;[FAIL] Diff vid byte %d: Org=0x%02X, Avkodad=0x%02X\n&amp;quot;, i, original_data[i], final_data[i]);&lt;br /&gt;
                success = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Stäng alla strömmar ordentligt. */&lt;br /&gt;
    fclose(orig_stream);&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        fclose(encoded_stream);&lt;br /&gt;
        fclose(corrupted_stream);&lt;br /&gt;
    } else {&lt;br /&gt;
        fclose(encoded_stream);&lt;br /&gt;
    }&lt;br /&gt;
    fclose(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* Skriv ut slutrapport för testet. */&lt;br /&gt;
    if (success) {&lt;br /&gt;
        if (inject_errors) {&lt;br /&gt;
            printf(&amp;quot;[SUCCESS] Viterbi-algoritmens felkorrigering lyckades! Alla bitfel lagades och datat matchar perfekt.\n&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
            printf(&amp;quot;[SUCCESS] Testet lyckades! Allt data matchar perfekt.\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// HJÄLPTEXT OCH MAIN&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void print_help(const char *prog_name) {&lt;br /&gt;
    printf(&amp;quot;Användning: %s [option]\n&amp;quot;, prog_name);&lt;br /&gt;
    printf(&amp;quot;Optioner:\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -e &amp;lt;infil &amp;gt;utfil   Kodar infil till utfil (Rate 1/2, K=7)\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -d &amp;lt;infil &amp;gt;utfil   Avkodar infil till utfil med Viterbi\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -h                 Visar denna hjälptext\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -t                 Kör internt test med 64 bytes slumpdata\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  --error            Kör internt test men injicerar bitfel för att testa felkorrigeringen\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    /* Säkerställ att användaren har skickat med exakt ett kommandoradsargument. */&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Kontrollera vilket argument användaren skickade och anropa rätt funktion. */&lt;br /&gt;
    if (strcmp(argv[1], &amp;quot;-e&amp;quot;) == 0) {&lt;br /&gt;
        encode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-d&amp;quot;) == 0) {&lt;br /&gt;
        decode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-h&amp;quot;) == 0) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-t&amp;quot;) == 0) {&lt;br /&gt;
        run_test(false);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;--error&amp;quot;) == 0) {&lt;br /&gt;
        run_test(true);&lt;br /&gt;
    } else {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Fel: Okänd parameter &#039;%s&#039;\n&amp;quot;, argv[1]);&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1909</id>
		<title>Viterbi</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1909"/>
		<updated>2026-06-23T17:47:20Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Bakgrund = &lt;br /&gt;
&lt;br /&gt;
== Viterbis algoritm – Hur vi hittar rätt signal i bruset ==&lt;br /&gt;
&lt;br /&gt;
Inom amatörradion och digital signalbehandling stöter vi ofta på problemet med brus och fading. När vi skickar digital data över etern, till exempel med modernare digitala trafiksätt eller vid satellitkommunikation, använder vi ofta något som kallas för faltningskodning (convolutional coding). Det är en metod där varje sänd bit beror på både den nuvarande biten och ett antal tidigare bitar. Detta skapar ett mönster, ett beroende, som gör signalen robust.&lt;br /&gt;
&lt;br /&gt;
Men hur avkodar vi detta på mottagarsidan när atmosfären har ställt till det och lagt till en massa brus? Det är här Viterbis algoritm kommer in i bilden.&lt;br /&gt;
&lt;br /&gt;
Kort sagt är Viterbialgoritmen en metod för att hitta den mest troliga sekvensen av dolda tillstånd (det vi faktiskt sände) baserat på en serie observerade händelser (den brusiga signalen vi tog emot). Istället för att gissa bit för bit, tittar algoritmen på hela sammanhanget.&lt;br /&gt;
&lt;br /&gt;
== Trellis-diagrammet: Vägarna genom historien ==&lt;br /&gt;
&lt;br /&gt;
För att förstå hur algoritmen jobbar kan vi tänka oss ett nätverk av vägar, ett så kallat trellis-diagram.&lt;br /&gt;
&lt;br /&gt;
* Tillstånd (States): Radiosändarens &amp;quot;minne&amp;quot; kan vid varje given tidpunkt befinna sig i ett visst antal tillstånd (beroende på de senaste sända bitarna).&lt;br /&gt;
&lt;br /&gt;
* Övergångar: När en ny bit ska sändas hoppar sändaren till ett nytt tillstånd och skickar ut en specifik kombination av bitar i luften.&lt;br /&gt;
&lt;br /&gt;
== Hur funkar det? ==&lt;br /&gt;
&lt;br /&gt;
När mottagaren lyssnar får den in en sekvens som kanske är halvt dränkt i brus. En nolla kanske ser ut som en svag etta, och så vidare. Viterbialgoritmen börjar nu räkna på alla möjliga vägar genom detta nätverk av tillstånd över tid.&lt;br /&gt;
Så här jobbar algoritmen steg för steg:&lt;br /&gt;
&lt;br /&gt;
# Beräkna avstånd (Metric): För varje steg i tiden tittar algoritmen på de bitar som faktiskt togs emot och jämför dem med vad som borde ha sänts för varje tänkbar väg i diagrammet. Man mäter &amp;quot;avståndet&amp;quot; (ofta Hamming-avstånd för ettor och nollor, eller Euklidiskt avstånd om man kör &amp;quot;soft decision&amp;quot; där man mäter signalstyrkan mer exakt). Ju mindre avstånd, desto mer troligt är det att den vägen är den rätta.&lt;br /&gt;
&lt;br /&gt;
# Spara den bästa vägen (Add-Compare-Select): Till varje nytt tillstånd i diagrammet leder det oftast två olika vägar från det föregående steget. Algoritmen adderar det nya avståndet till det gamla, jämför de två alternativen, och väljer den väg som har lägst totalt avstånd (fel). Den andra vägen slängs bort för alltid. Det här är algoritmens stora styrka: den rensar hela tiden bort återvändsgränder så att beräkningsbördan inte exploderar.&lt;br /&gt;
&lt;br /&gt;
# Lås historiken (Traceback): När hela sekvensen (eller ett tillräckligt långt block) har tagits emot, tittar man på vilket slutgiltigt tillstånd som har det absolut lägsta totala felvärdet. Sedan backar man bakåt genom de sparade &amp;quot;bästa vägarna&amp;quot; till början.&lt;br /&gt;
&lt;br /&gt;
Resultatet blir den absolut mest sannolika sekvensen av bitar som sändaren faktiskt skickade ut, trots att det var fullt av bitfel under resans gång.&lt;br /&gt;
&lt;br /&gt;
= Implementation =&lt;br /&gt;
&lt;br /&gt;
== Modern C ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Anders Sikvall 2026 Version 1.0&lt;br /&gt;
 * &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
 * &lt;br /&gt;
 * Implementation i C av en standard Viterbi-algoritm som används bl.a. av &lt;br /&gt;
 * Qualcomm och NASA och är välpublicerad.&lt;br /&gt;
 *&lt;br /&gt;
 * PUBLIC DOMAIN FOR EDUCATIONAL PURPOSES&lt;br /&gt;
 *&lt;br /&gt;
 * Kompilera med gcc -o viterbi viterbi.c&lt;br /&gt;
 *&lt;br /&gt;
 * ANVÄNDNING&lt;br /&gt;
 *         viterbi -e &amp;lt;indatastream &amp;gt;utdatastream   Kodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -d &amp;lt;indatastream &amp;gt;utdatastream   Avkodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -t                               Utför en intern test på 64 randomiserade oktetter&lt;br /&gt;
 *         viterbi -h                               Visar en kort hjälptext&lt;br /&gt;
 *         viterbi --error                          Samma sak som -t men introducerar 3 flippade bitpar&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Constraint length (K=7) innebär att skiftregistret minns de senaste 7 bitarna. */&lt;br /&gt;
#define K 7&lt;br /&gt;
&lt;br /&gt;
/* Antalet tillstånd i trellis-diagrammet bestäms av de senaste K-1 bitarna.&lt;br /&gt;
   2^(7-1) = 2^6 = 64 unika tillstånd totalt. */&lt;br /&gt;
#define NUM_STATES (1 &amp;lt;&amp;lt; (K - 1))&lt;br /&gt;
&lt;br /&gt;
/* Standard NASA/Qualcomm-polynom för Rate 1/2 konvolutionskodning.&lt;br /&gt;
   POLY_G0 motsvarar oktalt 151 (1001111 binärt).&lt;br /&gt;
   POLY_G1 motsvarar oktalt 113 (1101101 binärt).&lt;br /&gt;
   Dessa maskar används för att bestämma vilka bitar i tillståndet som XOR-sammanfogas. */&lt;br /&gt;
const uint8_t POLY_G0 = 0x4F;&lt;br /&gt;
const uint8_t POLY_G1 = 0x6D;&lt;br /&gt;
&lt;br /&gt;
/* Hårdvaruoptimerad paritetsberäkning.&lt;br /&gt;
   Räknar antalet satta bitar (1:or) i ett tal och returnerar 1 om antalet är udda,&lt;br /&gt;
   eller 0 om antalet är jämnt (motsvarar en kedja av XOR-grindar). */&lt;br /&gt;
static inline uint8_t parity(uint8_t x) {&lt;br /&gt;
    return __builtin_parity(x) &amp;amp; 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// KODARE (-e)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void encode(FILE *in, FILE *out) {&lt;br /&gt;
    /* encoder_state lagrar historiken av de senaste K-1 (6) bitarna. */&lt;br /&gt;
    uint8_t encoder_state = 0;&lt;br /&gt;
&lt;br /&gt;
    /* out_byte används som en tillfällig buffer för att samla ihop bitar till en hel byte. */&lt;br /&gt;
    uint8_t out_byte = 0;&lt;br /&gt;
&lt;br /&gt;
    /* bit_count håller koll på hur många bitar som för tillfället ligger i utmatningsbufferten. */&lt;br /&gt;
    int bit_count = 0;&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    /* Loopa igenom infilen tecken för tecken (byte för byte). */&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
&lt;br /&gt;
        /* Gå igenom varje enskild bit i den inlästa byten, från MSB (bit 7) till LSB (bit 0). */&lt;br /&gt;
        for (int i = 7; i &amp;gt;= 0; i--) {&lt;br /&gt;
&lt;br /&gt;
            /* Isolera biten på position i. */&lt;br /&gt;
            uint8_t bit = (ch &amp;gt;&amp;gt; i) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            /* Skapa det nuvarande ordet genom att skifta upp det gamla tillståndet till vänster&lt;br /&gt;
               och lägga till den nya inkommande biten som den lägsta biten (LSB). */&lt;br /&gt;
            uint8_t current_word = (encoder_state &amp;lt;&amp;lt; 1) | bit;&lt;br /&gt;
&lt;br /&gt;
            /* Beräkna de två paritetsbitarna (out0 och out1) genom att applicera polynommaskerna&lt;br /&gt;
               och köra XOR (paritet) på resultatet. */&lt;br /&gt;
            uint8_t out0 = parity(current_word &amp;amp; POLY_G0);&lt;br /&gt;
            uint8_t out1 = parity(current_word &amp;amp; POLY_G1);&lt;br /&gt;
&lt;br /&gt;
            /* Knuffa in den första paritetsbiten (out0) i utmatningsbyten. */&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out0;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
&lt;br /&gt;
            /* Om vi har fyllt en hel byte (8 bitar), skriv ut den till utmatningsströmmen. */&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Knuffa in den andra paritetsbiten (out1) i utmatningsbyten. */&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out1;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
&lt;br /&gt;
            /* Kontrollera återigen om utmatningsbyten blev full efter den andra biten. */&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Uppdatera kodarens interna tillstånd inför nästa bit.&lt;br /&gt;
               Vi sparar endast de lägsta K-1 bitarna med hjälp av en bitmask (63 = 0x3F). */&lt;br /&gt;
            encoder_state = current_word &amp;amp; (NUM_STATES - 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Om det finns kvarvarande bitar i bufferten när filen slutar,&lt;br /&gt;
       skiftar vi dem till vänster så att de ligger rätt, och skriver ut den sista ofullständiga byten. */&lt;br /&gt;
    if (bit_count &amp;gt; 0) {&lt;br /&gt;
        out_byte &amp;lt;&amp;lt;= (8 - bit_count);&lt;br /&gt;
        fputc(out_byte, out);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// AVKODARE (-d)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void decode(FILE *in, FILE *out) {&lt;br /&gt;
    /* Startkapacitet för historikmatrisen (antalet bit-steg). Den växer linjärt vid behov. */&lt;br /&gt;
    size_t history_capacity = 1024;&lt;br /&gt;
    size_t step = 0;&lt;br /&gt;
&lt;br /&gt;
    /* Allokera historikmatrisen dynamiskt. Det är en 2D-array av storleken [kapacitet][64].&lt;br /&gt;
       Den sparar överlevnadsbesluten (0 eller 1) för varje tillstånd vid varje givet tidssteg. */&lt;br /&gt;
    uint8_t (*history)[NUM_STATES] = malloc(history_capacity * sizeof(*history));&lt;br /&gt;
    if (!history) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* path_metrics lagrar den ackumulerade felkostnaden (Hammingavståndet) för de 64 nuvarande tillstånden. */&lt;br /&gt;
    uint32_t path_metrics[NUM_STATES];&lt;br /&gt;
&lt;br /&gt;
    /* old_metrics lagrar en oförändrad kopia av kostnaderna från föregående tidssteg. */&lt;br /&gt;
    uint32_t old_metrics[NUM_STATES];&lt;br /&gt;
&lt;br /&gt;
    /* Initiera alla tillstånd till en mycket hög felkostnad (&amp;quot;oändlighet&amp;quot;).&lt;br /&gt;
       Undantaget är tillstånd 0, vilket är det kända och garanterade starttillståndet (kostnad 0). */&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        path_metrics[i] = 1e6;&lt;br /&gt;
    }&lt;br /&gt;
    path_metrics[0] = 0;&lt;br /&gt;
&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 1: Framåtpass (Forward Pass / Add-Compare-Select)&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Läs den kodade strömmen byte för byte. */&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
&lt;br /&gt;
        /* Varje inläst byte innehåller 4 bitpar (totalt 8 kodade bitar), vilket motsvarar 4 tidssteg. */&lt;br /&gt;
        for (int bit_pair = 3; bit_pair &amp;gt;= 0; bit_pair--) {&lt;br /&gt;
&lt;br /&gt;
            /* Extrahera de två bitarna (b0 och b1) som hör till det aktuella steget. */&lt;br /&gt;
            uint8_t b0 = (ch &amp;gt;&amp;gt; (bit_pair * 2 + 1)) &amp;amp; 1;&lt;br /&gt;
            uint8_t b1 = (ch &amp;gt;&amp;gt; (bit_pair * 2)) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            /* Om vår historikmatris är full, fördubblar vi dess storlek dynamiskt med realloc.&lt;br /&gt;
               Detta gör att programmet kan hantera obegränsat stora filer och pipes. */&lt;br /&gt;
            if (step &amp;gt;= history_capacity) {&lt;br /&gt;
                history_capacity *= 2;&lt;br /&gt;
                uint8_t (*new_history)[NUM_STATES] = realloc(history, history_capacity * sizeof(*history));&lt;br /&gt;
                if (!new_history) {&lt;br /&gt;
                    free(history);&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
                history = new_history;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Kopiera över nuvarande path_metrics till old_metrics inför beräkningen av nästa steg. */&lt;br /&gt;
            memcpy(old_metrics, path_metrics, sizeof(path_metrics));&lt;br /&gt;
&lt;br /&gt;
            /* Loopa igenom alla 64 möjliga måltillstånd (nästa tillstånd) i trellis-diagrammet. */&lt;br /&gt;
            for (int state = 0; state &amp;lt; NUM_STATES; state++) {&lt;br /&gt;
&lt;br /&gt;
                /* Eftersom vi skiftar till vänster, kan vi räkna ut vilka två föregående tillstånd&lt;br /&gt;
                   (prev0 och prev1) som kan ha skiftat in i det nuvarande tillståndet. */&lt;br /&gt;
                int prev0 = state &amp;gt;&amp;gt; 1;&lt;br /&gt;
                int prev1 = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
&lt;br /&gt;
                /* Den inbit som krävdes för att nå &#039;state&#039; är identisk med nuvarande tillstånds LSB. */&lt;br /&gt;
                uint8_t input_bit = state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
                /* --- Beräkna förväntade bitar och Hammingavstånd för VÄG 0 (från prev0) --- */&lt;br /&gt;
                uint8_t word0 = (prev0 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp0_0 = parity(word0 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp0_1 = parity(word0 &amp;amp; POLY_G1);&lt;br /&gt;
                /* Kostnaden ökar med 1 för varje bit som inte matchar de faktiskt mottagna bitarna. */&lt;br /&gt;
                uint32_t cost0 = (exp0_0 != b0) + (exp0_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                /* --- Beräkna förväntade bitar och Hammingavstånd för VÄG 1 (från prev1) --- */&lt;br /&gt;
                uint8_t word1 = (prev1 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp1_0 = parity(word1 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp1_1 = parity(word1 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost1 = (exp1_0 != b0) + (exp1_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                /* Addera det gamla ackumulerade felet med det nya övergångsfelet för båda vägarna. */&lt;br /&gt;
                uint32_t m0 = old_metrics[prev0] + cost0;&lt;br /&gt;
                uint32_t m1 = old_metrics[prev1] + cost1;&lt;br /&gt;
&lt;br /&gt;
                /* (Compare &amp;amp; Select): Välj den väg som har lägst totalt fel.&lt;br /&gt;
                   Spara det nya lägsta felet i path_metrics, och dokumentera valet (0 eller 1)&lt;br /&gt;
                   i historikmatrisen för att kunna återskapa vägen baklänges senare. */&lt;br /&gt;
                if (m0 &amp;lt;= m1) {&lt;br /&gt;
                    path_metrics[state] = m0;&lt;br /&gt;
                    history[step][state] = 0;&lt;br /&gt;
                } else {&lt;br /&gt;
                    path_metrics[state] = m1;&lt;br /&gt;
                    history[step][state] = 1;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            /* Öka räknaren för antal avklarade bit-steg. */&lt;br /&gt;
            step++;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Om vi inte lyckades läsa några bitar alls, städa upp och avsluta. */&lt;br /&gt;
    if (step == 0) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 2: Hitta bästa sluttillstånd&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Sök igenom alla 64 tillstånd vid slutet av strömmen för att hitta det tillstånd&lt;br /&gt;
       som har samlat på sig minst antal fel (lägst metric). */&lt;br /&gt;
    int curr_state = 0;&lt;br /&gt;
    uint32_t min_m = path_metrics[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        if (path_metrics[i] &amp;lt; min_m) {&lt;br /&gt;
            min_m = path_metrics[i];&lt;br /&gt;
            curr_state = i;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 3: Bakåtspårning (Traceback)&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Allokera en temporär array för att spara de avkodade bitarna.&lt;br /&gt;
       Eftersom vi vandrar baklänges kommer vi att fylla denna array bakifrån. */&lt;br /&gt;
    uint8_t *decoded_bits = malloc(step);&lt;br /&gt;
    if (!decoded_bits) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Vandra baklänges genom historiken från det sista tidssteget ner till noll. */&lt;br /&gt;
    for (int s = (int)step - 1; s &amp;gt;= 0; s--) {&lt;br /&gt;
&lt;br /&gt;
        /* Den ursprungliga inbiten är alltid LSB (bit 0) i det tillstånd vi står i. */&lt;br /&gt;
        decoded_bits[s] = curr_state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
        /* Hämta beslutet (0 eller 1) som fattades för detta tillstånd i tidssteg s. */&lt;br /&gt;
        int decision = history[s][curr_state];&lt;br /&gt;
        int prev0 = curr_state &amp;gt;&amp;gt; 1;&lt;br /&gt;
&lt;br /&gt;
        /* Rekonstruera vilket tillstånd vi kom ifrån baserat på det sparade beslutet. */&lt;br /&gt;
        if (decision == 0) {&lt;br /&gt;
            curr_state = prev0;&lt;br /&gt;
        } else {&lt;br /&gt;
            curr_state = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    // STEG 4: Återpacka till bytes och skriv ut&lt;br /&gt;
    // ------------------------------------------------------------------------&lt;br /&gt;
    /* Bitarna ligger nu i korrekt kronologisk ordning i decoded_bits-arrayen.&lt;br /&gt;
       Vi loopar igenom dem och packar ihop dem 8 och 8 till vanliga bytes igen. */&lt;br /&gt;
    uint8_t current_byte = 0;&lt;br /&gt;
    for (size_t i = 0; i &amp;lt; step; i++) {&lt;br /&gt;
        current_byte = (current_byte &amp;lt;&amp;lt; 1) | decoded_bits[i];&lt;br /&gt;
&lt;br /&gt;
        /* Varje gång vi har samlat ihop 8 bitar skriver vi ut byten till utfilen/stdout. */&lt;br /&gt;
        if ((i + 1) % 8 == 0) {&lt;br /&gt;
            fputc(current_byte, out);&lt;br /&gt;
            current_byte = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Frigör det dynamiskt allokerade minnet. */&lt;br /&gt;
    free(decoded_bits);&lt;br /&gt;
    free(history);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// INTERNT TEST (-t OCH --error)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void run_test(bool inject_errors) {&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        printf(&amp;quot;[*] Startar internt test med FELKORRIGERING (64 bytes slumpdata + 3 bitpar flippade)...\n&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
        printf(&amp;quot;[*] Startar internt test (64 bytes randomiserat data)...\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Skapa en array med 64 bytes slumpmässigt data. */&lt;br /&gt;
    uint8_t original_data[64];&lt;br /&gt;
    srand((unsigned int)time(NULL));&lt;br /&gt;
    for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
        original_data[i] = rand() % 256;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* tmpfile() skapar temporära filer som körs i RAM/hårddisk och raderas automatiskt vid stängning. */&lt;br /&gt;
    FILE *orig_stream = tmpfile();&lt;br /&gt;
    FILE *encoded_stream = tmpfile();&lt;br /&gt;
    FILE *corrupted_stream = tmpfile();&lt;br /&gt;
    FILE *decoded_stream = tmpfile();&lt;br /&gt;
&lt;br /&gt;
    if (!orig_stream || !encoded_stream || !corrupted_stream || !decoded_stream) {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Kunde inte skapa temporära filer.\n&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Skriv originaldata till startströmmen och spola tillbaka pekaren till början. */&lt;br /&gt;
    fwrite(original_data, 1, 64, orig_stream);&lt;br /&gt;
    rewind(orig_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 1. Kör kodningen (64 bytes rådata blir exakt 128 bytes packad bitström). */&lt;br /&gt;
    encode(orig_stream, encoded_stream);&lt;br /&gt;
    rewind(encoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 2. Hantering av felinjicering (--error). */&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        uint8_t encoded_data[128];&lt;br /&gt;
&lt;br /&gt;
        /* Läs in hela den kodade strömmen till en lokal array. */&lt;br /&gt;
        if (fread(encoded_data, 1, 128, encoded_stream) == 128) {&lt;br /&gt;
&lt;br /&gt;
            /* Välj ut 3 slumpmässiga bytes och flippa en slumpmässig bit i var och en av dem. */&lt;br /&gt;
            for (int i = 0; i &amp;lt; 3; i++) {&lt;br /&gt;
                int byte_index = rand() % 128;&lt;br /&gt;
                int bit_index = rand() % 8;&lt;br /&gt;
&lt;br /&gt;
                /* XOR-operatör (^) flippar den valda biten (0 blir 1, 1 blir 0). */&lt;br /&gt;
                encoded_data[byte_index] ^= (1 &amp;lt;&amp;lt; bit_index);&lt;br /&gt;
                printf(&amp;quot;[i] Introducerade bitfel i kodad byte %d, bit %d\n&amp;quot;, byte_index, bit_index);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        /* Skriv ut det saboterade datat till den korrupta strömmen. */&lt;br /&gt;
        fwrite(encoded_data, 1, 128, corrupted_stream);&lt;br /&gt;
        rewind(corrupted_stream);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Om inga fel ska testas, pekar vi bara vidare till den felfria strömmen direkt. */&lt;br /&gt;
        corrupted_stream = encoded_stream;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* 3. Kör avkodaren på dataströmmen. */&lt;br /&gt;
    decode(corrupted_stream, decoded_stream);&lt;br /&gt;
    rewind(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* 4. Läs tillbaka det färdigavkodade datat och verifiera resultatet. */&lt;br /&gt;
    uint8_t final_data[64];&lt;br /&gt;
    size_t read_bytes = fread(final_data, 1, 64, decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    bool success = true;&lt;br /&gt;
    if (read_bytes != 64) {&lt;br /&gt;
        printf(&amp;quot;[FAIL] Avkodade fel antal bytes: %zu (förväntade 64)\n&amp;quot;, read_bytes);&lt;br /&gt;
        success = false;&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Jämför ursprunglig array med den avkodade arrayen byte för byte. */&lt;br /&gt;
        for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
            if (original_data[i] != final_data[i]) {&lt;br /&gt;
                printf(&amp;quot;[FAIL] Diff vid byte %d: Org=0x%02X, Avkodad=0x%02X\n&amp;quot;, i, original_data[i], final_data[i]);&lt;br /&gt;
                success = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Stäng alla strömmar ordentligt. */&lt;br /&gt;
    fclose(orig_stream);&lt;br /&gt;
    if (inject_errors) {&lt;br /&gt;
        fclose(encoded_stream);&lt;br /&gt;
        fclose(corrupted_stream);&lt;br /&gt;
    } else {&lt;br /&gt;
        fclose(encoded_stream);&lt;br /&gt;
    }&lt;br /&gt;
    fclose(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    /* Skriv ut slutrapport för testet. */&lt;br /&gt;
    if (success) {&lt;br /&gt;
        if (inject_errors) {&lt;br /&gt;
            printf(&amp;quot;[SUCCESS] Viterbi-algoritmens felkorrigering lyckades! Alla bitfel lagades och datat matchar perfekt.\n&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
            printf(&amp;quot;[SUCCESS] Testet lyckades! Allt data matchar perfekt.\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// HJÄLPTEXT OCH MAIN&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void print_help(const char *prog_name) {&lt;br /&gt;
    printf(&amp;quot;Användning: %s [option]\n&amp;quot;, prog_name);&lt;br /&gt;
    printf(&amp;quot;Optioner:\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -e &amp;lt;infil &amp;gt;utfil   Kodar infil till utfil (Rate 1/2, K=7)\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -d &amp;lt;infil &amp;gt;utfil   Avkodar infil till utfil med Viterbi\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -h                 Visar denna hjälptext\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -t                 Kör internt test med 64 bytes slumpdata\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  --error            Kör internt test men injicerar bitfel för att testa felkorrigeringen\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    /* Säkerställ att användaren har skickat med exakt ett kommandoradsargument. */&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Kontrollera vilket argument användaren skickade och anropa rätt funktion. */&lt;br /&gt;
    if (strcmp(argv[1], &amp;quot;-e&amp;quot;) == 0) {&lt;br /&gt;
        encode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-d&amp;quot;) == 0) {&lt;br /&gt;
        decode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-h&amp;quot;) == 0) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-t&amp;quot;) == 0) {&lt;br /&gt;
        run_test(false);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;--error&amp;quot;) == 0) {&lt;br /&gt;
        run_test(true);&lt;br /&gt;
    } else {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Fel: Okänd parameter &#039;%s&#039;\n&amp;quot;, argv[1]);&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1908</id>
		<title>Viterbi</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1908"/>
		<updated>2026-06-23T17:41:16Z</updated>

		<summary type="html">&lt;p&gt;Anders: /* Bakgrund */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Bakgrund = &lt;br /&gt;
&lt;br /&gt;
== Viterbis algoritm – Hur vi hittar rätt signal i bruset ==&lt;br /&gt;
&lt;br /&gt;
Inom amatörradion och digital signalbehandling stöter vi ofta på problemet med brus och fading. När vi skickar digital data över etern, till exempel med modernare digitala trafiksätt eller vid satellitkommunikation, använder vi ofta något som kallas för faltningskodning (convolutional coding). Det är en metod där varje sänd bit beror på både den nuvarande biten och ett antal tidigare bitar. Detta skapar ett mönster, ett beroende, som gör signalen robust.&lt;br /&gt;
&lt;br /&gt;
Men hur avkodar vi detta på mottagarsidan när atmosfären har ställt till det och lagt till en massa brus? Det är här Viterbis algoritm kommer in i bilden.&lt;br /&gt;
&lt;br /&gt;
Kort sagt är Viterbialgoritmen en metod för att hitta den mest troliga sekvensen av dolda tillstånd (det vi faktiskt sände) baserat på en serie observerade händelser (den brusiga signalen vi tog emot). Istället för att gissa bit för bit, tittar algoritmen på hela sammanhanget.&lt;br /&gt;
&lt;br /&gt;
== Trellis-diagrammet: Vägarna genom historien ==&lt;br /&gt;
&lt;br /&gt;
För att förstå hur algoritmen jobbar kan vi tänka oss ett nätverk av vägar, ett så kallat trellis-diagram.&lt;br /&gt;
&lt;br /&gt;
* Tillstånd (States): Radiosändarens &amp;quot;minne&amp;quot; kan vid varje given tidpunkt befinna sig i ett visst antal tillstånd (beroende på de senaste sända bitarna).&lt;br /&gt;
&lt;br /&gt;
* Övergångar: När en ny bit ska sändas hoppar sändaren till ett nytt tillstånd och skickar ut en specifik kombination av bitar i luften.&lt;br /&gt;
&lt;br /&gt;
== Hur funkar det? ==&lt;br /&gt;
&lt;br /&gt;
När mottagaren lyssnar får den in en sekvens som kanske är halvt dränkt i brus. En nolla kanske ser ut som en svag etta, och så vidare. Viterbialgoritmen börjar nu räkna på alla möjliga vägar genom detta nätverk av tillstånd över tid.&lt;br /&gt;
Så här jobbar algoritmen steg för steg:&lt;br /&gt;
&lt;br /&gt;
# Beräkna avstånd (Metric): För varje steg i tiden tittar algoritmen på de bitar som faktiskt togs emot och jämför dem med vad som borde ha sänts för varje tänkbar väg i diagrammet. Man mäter &amp;quot;avståndet&amp;quot; (ofta Hamming-avstånd för ettor och nollor, eller Euklidiskt avstånd om man kör &amp;quot;soft decision&amp;quot; där man mäter signalstyrkan mer exakt). Ju mindre avstånd, desto mer troligt är det att den vägen är den rätta.&lt;br /&gt;
&lt;br /&gt;
# Spara den bästa vägen (Add-Compare-Select): Till varje nytt tillstånd i diagrammet leder det oftast två olika vägar från det föregående steget. Algoritmen adderar det nya avståndet till det gamla, jämför de två alternativen, och väljer den väg som har lägst totalt avstånd (fel). Den andra vägen slängs bort för alltid. Det här är algoritmens stora styrka: den rensar hela tiden bort återvändsgränder så att beräkningsbördan inte exploderar.&lt;br /&gt;
&lt;br /&gt;
# Lås historiken (Traceback): När hela sekvensen (eller ett tillräckligt långt block) har tagits emot, tittar man på vilket slutgiltigt tillstånd som har det absolut lägsta totala felvärdet. Sedan backar man bakåt genom de sparade &amp;quot;bästa vägarna&amp;quot; till början.&lt;br /&gt;
&lt;br /&gt;
Resultatet blir den absolut mest sannolika sekvensen av bitar som sändaren faktiskt skickade ut, trots att det var fullt av bitfel under resans gång.&lt;br /&gt;
&lt;br /&gt;
= Implementation =&lt;br /&gt;
&lt;br /&gt;
== Modern C ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Anders Sikvall 2026 Version 1.0&lt;br /&gt;
 * &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
 * &lt;br /&gt;
 * Implementation i C av en standard Viterbi-algoritm som används bl.a. av &lt;br /&gt;
 * Qualcomm och NASA och är välpublicerad.&lt;br /&gt;
 *&lt;br /&gt;
 * PUBLIC DOMAIN FOR EDUCATIONAL PURPOSES&lt;br /&gt;
 *&lt;br /&gt;
 * Kompilera med gcc -o viterbi viterbi.c&lt;br /&gt;
 *&lt;br /&gt;
 * ANVÄNDNING&lt;br /&gt;
 *         viterbi -e &amp;lt;indatastream &amp;gt;utdatastream   Kodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -d &amp;lt;indatastream &amp;gt;utdatastream   Avkodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -t                               Utför en intern test på 64 randomiserade oktetter&lt;br /&gt;
 *         viterbi -h                               Visar en kort hjälptext&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define K 7&lt;br /&gt;
#define NUM_STATES (1 &amp;lt;&amp;lt; (K - 1))&lt;br /&gt;
&lt;br /&gt;
// Standard NASA/Qualcomm-polynom (Rate 1/2, K=7)&lt;br /&gt;
const uint8_t POLY_G0 = 0x4F; // 1001111 i binärt&lt;br /&gt;
const uint8_t POLY_G1 = 0x6D; // 1101101 i binärt&lt;br /&gt;
&lt;br /&gt;
// Hårdvaruoptimerad paritetsberäkning&lt;br /&gt;
static inline uint8_t parity(uint8_t x) {&lt;br /&gt;
    return __builtin_parity(x) &amp;amp; 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// KODARE (-e)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void encode(FILE *in, FILE *out) {&lt;br /&gt;
    uint8_t encoder_state = 0;&lt;br /&gt;
    uint8_t out_byte = 0;&lt;br /&gt;
    int bit_count = 0;&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
        // Processa varje bit från MSB till LSB&lt;br /&gt;
        for (int i = 7; i &amp;gt;= 0; i--) {&lt;br /&gt;
            uint8_t bit = (ch &amp;gt;&amp;gt; i) &amp;amp; 1;&lt;br /&gt;
            uint8_t current_word = (encoder_state &amp;lt;&amp;lt; 1) | bit;&lt;br /&gt;
&lt;br /&gt;
            // Beräkna de två paritetsbitarna&lt;br /&gt;
            uint8_t out0 = parity(current_word &amp;amp; POLY_G0);&lt;br /&gt;
            uint8_t out1 = parity(current_word &amp;amp; POLY_G1);&lt;br /&gt;
&lt;br /&gt;
            // Packa bit 0&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out0;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Packa bit 1&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out1;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Uppdatera skiftregistret (behåll K-1 bitar)&lt;br /&gt;
            encoder_state = current_word &amp;amp; (NUM_STATES - 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Töm resterande bitar i sista byten vid behov&lt;br /&gt;
    if (bit_count &amp;gt; 0) {&lt;br /&gt;
        out_byte &amp;lt;&amp;lt;= (8 - bit_count);&lt;br /&gt;
        fputc(out_byte, out);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// AVKODARE (-d)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void decode(FILE *in, FILE *out) {&lt;br /&gt;
    size_t history_capacity = 1024;&lt;br /&gt;
    size_t step = 0;&lt;br /&gt;
&lt;br /&gt;
    uint8_t (*history)[NUM_STATES] = malloc(history_capacity * sizeof(*history));&lt;br /&gt;
    if (!history) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    uint32_t path_metrics[NUM_STATES];&lt;br /&gt;
    uint32_t old_metrics[NUM_STATES];&lt;br /&gt;
    &lt;br /&gt;
    // Initiera felkostnader (tillstånd 0 är garanterad start)&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        path_metrics[i] = 1e6;&lt;br /&gt;
    }&lt;br /&gt;
    path_metrics[0] = 0;&lt;br /&gt;
&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    // 1. Framåtpass (Forward Pass / Add-Compare-Select)&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
        for (int bit_pair = 3; bit_pair &amp;gt;= 0; bit_pair--) {&lt;br /&gt;
            uint8_t b0 = (ch &amp;gt;&amp;gt; (bit_pair * 2 + 1)) &amp;amp; 1;&lt;br /&gt;
            uint8_t b1 = (ch &amp;gt;&amp;gt; (bit_pair * 2)) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            // Expandera historikbufferten dynamiskt om det behövs&lt;br /&gt;
            if (step &amp;gt;= history_capacity) {&lt;br /&gt;
                history_capacity *= 2;&lt;br /&gt;
                uint8_t (*new_history)[NUM_STATES] = realloc(history, history_capacity * sizeof(*history));&lt;br /&gt;
                if (!new_history) {&lt;br /&gt;
                    free(history);&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
                history = new_history;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            memcpy(old_metrics, path_metrics, sizeof(path_metrics));&lt;br /&gt;
&lt;br /&gt;
            // Gå igenom alla möjliga måltillstånd&lt;br /&gt;
            for (int state = 0; state &amp;lt; NUM_STATES; state++) {&lt;br /&gt;
                int prev0 = state &amp;gt;&amp;gt; 1;&lt;br /&gt;
                int prev1 = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
                uint8_t input_bit = state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
                // Väg 0&lt;br /&gt;
                uint8_t word0 = (prev0 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp0_0 = parity(word0 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp0_1 = parity(word0 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost0 = (exp0_0 != b0) + (exp0_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                // Väg 1&lt;br /&gt;
                uint8_t word1 = (prev1 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp1_0 = parity(word1 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp1_1 = parity(word1 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost1 = (exp1_0 != b0) + (exp1_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                uint32_t m0 = old_metrics[prev0] + cost0;&lt;br /&gt;
                uint32_t m1 = old_metrics[prev1] + cost1;&lt;br /&gt;
&lt;br /&gt;
                // Välj den bästa överlevande vägen&lt;br /&gt;
                if (m0 &amp;lt;= m1) {&lt;br /&gt;
                    path_metrics[state] = m0;&lt;br /&gt;
                    history[step][state] = 0;&lt;br /&gt;
                } else {&lt;br /&gt;
                    path_metrics[state] = m1;&lt;br /&gt;
                    history[step][state] = 1;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            step++;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (step == 0) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 2. Hitta bästa sluttillstånd (lägsta metric)&lt;br /&gt;
    int curr_state = 0;&lt;br /&gt;
    uint32_t min_m = path_metrics[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        if (path_metrics[i] &amp;lt; min_m) {&lt;br /&gt;
            min_m = path_metrics[i];&lt;br /&gt;
            curr_state = i;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 3. Bakåtspårning (Traceback)&lt;br /&gt;
    uint8_t *decoded_bits = malloc(step);&lt;br /&gt;
    if (!decoded_bits) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (int s = (int)step - 1; s &amp;gt;= 0; s--) {&lt;br /&gt;
        decoded_bits[s] = curr_state &amp;amp; 1;&lt;br /&gt;
        int decision = history[s][curr_state];&lt;br /&gt;
        int prev0 = curr_state &amp;gt;&amp;gt; 1;&lt;br /&gt;
        &lt;br /&gt;
        if (decision == 0) {&lt;br /&gt;
            curr_state = prev0;&lt;br /&gt;
        } else {&lt;br /&gt;
            curr_state = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 4. Återpacka till bytes och skriv ut till strömmen&lt;br /&gt;
    uint8_t current_byte = 0;&lt;br /&gt;
    for (size_t i = 0; i &amp;lt; step; i++) {&lt;br /&gt;
        current_byte = (current_byte &amp;lt;&amp;lt; 1) | decoded_bits[i];&lt;br /&gt;
        if ((i + 1) % 8 == 0) {&lt;br /&gt;
            fputc(current_byte, out);&lt;br /&gt;
            current_byte = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    free(decoded_bits);&lt;br /&gt;
    free(history);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// INTERNT TEST (-t)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void run_test(void) {&lt;br /&gt;
    printf(&amp;quot;[*] Startar internt test (64 bytes randomiserat data)...\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    uint8_t original_data[64];&lt;br /&gt;
    srand((unsigned int)time(NULL));&lt;br /&gt;
    for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
        original_data[i] = rand() % 256;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    FILE *orig_stream = tmpfile();&lt;br /&gt;
    FILE *encoded_stream = tmpfile();&lt;br /&gt;
    FILE *decoded_stream = tmpfile();&lt;br /&gt;
&lt;br /&gt;
    if (!orig_stream || !encoded_stream || !decoded_stream) {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Kunde inte skapa temporära filer.\n&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fwrite(original_data, 1, 64, orig_stream);&lt;br /&gt;
    rewind(orig_stream);&lt;br /&gt;
&lt;br /&gt;
    encode(orig_stream, encoded_stream);&lt;br /&gt;
    rewind(encoded_stream);&lt;br /&gt;
&lt;br /&gt;
    decode(encoded_stream, decoded_stream);&lt;br /&gt;
    rewind(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    uint8_t final_data[64];&lt;br /&gt;
    size_t read_bytes = fread(final_data, 1, 64, decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    bool success = true;&lt;br /&gt;
    if (read_bytes != 64) {&lt;br /&gt;
        printf(&amp;quot;[FAIL] Avkodade fel antal bytes: %zu (förväntade 64)\n&amp;quot;, read_bytes);&lt;br /&gt;
        success = false;&lt;br /&gt;
    } else {&lt;br /&gt;
        for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
            if (original_data[i] != final_data[i]) {&lt;br /&gt;
                printf(&amp;quot;[FAIL] Diff vid byte %d: Org=0x%02X, Avkodad=0x%02X\n&amp;quot;, i, original_data[i], final_data[i]);&lt;br /&gt;
                success = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fclose(orig_stream);&lt;br /&gt;
    fclose(encoded_stream);&lt;br /&gt;
    fclose(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    if (success) {&lt;br /&gt;
        printf(&amp;quot;[SUCCESS] Testet lyckades! Allt data matchar perfekt.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// HJÄLPTEXT OCH MAIN&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void print_help(const char *prog_name) {&lt;br /&gt;
    printf(&amp;quot;Användning: %s [option]\n&amp;quot;, prog_name);&lt;br /&gt;
    printf(&amp;quot;Optioner:\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -e &amp;lt;infil &amp;gt;utfil   Kodar infil till utfil (Rate 1/2, K=7)\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -d &amp;lt;infil &amp;gt;utfil   Avkodar infil till utfil med Viterbi\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -h                 Visar denna hjälptext\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -t                 Kör internt test med 64 bytes slumpdata\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (strcmp(argv[1], &amp;quot;-e&amp;quot;) == 0) {&lt;br /&gt;
        encode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-d&amp;quot;) == 0) {&lt;br /&gt;
        decode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-h&amp;quot;) == 0) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-t&amp;quot;) == 0) {&lt;br /&gt;
        run_test();&lt;br /&gt;
    } else {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Fel: Okänd parameter &#039;%s&#039;\n&amp;quot;, argv[1]);&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1907</id>
		<title>Viterbi</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1907"/>
		<updated>2026-06-23T17:40:29Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Bakgrund = &lt;br /&gt;
&lt;br /&gt;
Viterbis algoritm – Hur vi hittar rätt signal i bruset&lt;br /&gt;
&lt;br /&gt;
Inom amatörradion och digital signalbehandling stöter vi ofta på problemet med brus och fading. När vi skickar digital data över etern, till exempel med modernare digitala trafiksätt eller vid satellitkommunikation, använder vi ofta något som kallas för faltningskodning (convolutional coding). Det är en metod där varje sänd bit beror på både den nuvarande biten och ett antal tidigare bitar. Detta skapar ett mönster, ett beroende, som gör signalen robust.&lt;br /&gt;
&lt;br /&gt;
Men hur avkodar vi detta på mottagarsidan när atmosfären har ställt till det och lagt till en massa brus? Det är här Viterbis algoritm kommer in i bilden.&lt;br /&gt;
&lt;br /&gt;
Kort sagt är Viterbialgoritmen en metod för att hitta den mest troliga sekvensen av dolda tillstånd (det vi faktiskt sände) baserat på en serie observerade händelser (den brusiga signalen vi tog emot). Istället för att gissa bit för bit, tittar algoritmen på hela sammanhanget.&lt;br /&gt;
Trellis-diagrammet: Vägarna genom historien&lt;br /&gt;
&lt;br /&gt;
För att förstå hur algoritmen jobbar kan vi tänka oss ett nätverk av vägar, ett så kallat trellis-diagram.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Tillstånd (States): Radiosändarens &amp;quot;minne&amp;quot; kan vid varje given tidpunkt befinna sig i ett visst antal tillstånd (beroende på de senaste sända bitarna).&lt;br /&gt;
&lt;br /&gt;
* Övergångar: När en ny bit ska sändas hoppar sändaren till ett nytt tillstånd och skickar ut en specifik kombination av bitar i luften.&lt;br /&gt;
&lt;br /&gt;
När mottagaren lyssnar får den in en sekvens som kanske är halvt dränkt i brus. En nolla kanske ser ut som en svag etta, och så vidare. Viterbialgoritmen börjar nu räkna på alla möjliga vägar genom detta nätverk av tillstånd över tid.&lt;br /&gt;
Så här jobbar algoritmen steg för steg:&lt;br /&gt;
&lt;br /&gt;
# Beräkna avstånd (Metric): För varje steg i tiden tittar algoritmen på de bitar som faktiskt togs emot och jämför dem med vad som borde ha sänts för varje tänkbar väg i diagrammet. Man mäter &amp;quot;avståndet&amp;quot; (ofta Hamming-avstånd för ettor och nollor, eller Euklidiskt avstånd om man kör &amp;quot;soft decision&amp;quot; där man mäter signalstyrkan mer exakt). Ju mindre avstånd, desto mer troligt är det att den vägen är den rätta.&lt;br /&gt;
&lt;br /&gt;
# Spara den bästa vägen (Add-Compare-Select): Till varje nytt tillstånd i diagrammet leder det oftast två olika vägar från det föregående steget. Algoritmen adderar det nya avståndet till det gamla, jämför de två alternativen, och väljer den väg som har lägst totalt avstånd (fel). Den andra vägen slängs bort för alltid. Det här är algoritmens stora styrka: den rensar hela tiden bort återvändsgränder så att beräkningsbördan inte exploderar.&lt;br /&gt;
&lt;br /&gt;
# Lås historiken (Traceback): När hela sekvensen (eller ett tillräckligt långt block) har tagits emot, tittar man på vilket slutgiltigt tillstånd som har det absolut lägsta totala felvärdet. Sedan backar man bakåt genom de sparade &amp;quot;bästa vägarna&amp;quot; till början.&lt;br /&gt;
&lt;br /&gt;
Resultatet blir den absolut mest sannolika sekvensen av bitar som sändaren faktiskt skickade ut, trots att det var fullt av bitfel under resans gång.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Implementation =&lt;br /&gt;
&lt;br /&gt;
== Modern C ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Anders Sikvall 2026 Version 1.0&lt;br /&gt;
 * &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
 * &lt;br /&gt;
 * Implementation i C av en standard Viterbi-algoritm som används bl.a. av &lt;br /&gt;
 * Qualcomm och NASA och är välpublicerad.&lt;br /&gt;
 *&lt;br /&gt;
 * PUBLIC DOMAIN FOR EDUCATIONAL PURPOSES&lt;br /&gt;
 *&lt;br /&gt;
 * Kompilera med gcc -o viterbi viterbi.c&lt;br /&gt;
 *&lt;br /&gt;
 * ANVÄNDNING&lt;br /&gt;
 *         viterbi -e &amp;lt;indatastream &amp;gt;utdatastream   Kodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -d &amp;lt;indatastream &amp;gt;utdatastream   Avkodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -t                               Utför en intern test på 64 randomiserade oktetter&lt;br /&gt;
 *         viterbi -h                               Visar en kort hjälptext&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define K 7&lt;br /&gt;
#define NUM_STATES (1 &amp;lt;&amp;lt; (K - 1))&lt;br /&gt;
&lt;br /&gt;
// Standard NASA/Qualcomm-polynom (Rate 1/2, K=7)&lt;br /&gt;
const uint8_t POLY_G0 = 0x4F; // 1001111 i binärt&lt;br /&gt;
const uint8_t POLY_G1 = 0x6D; // 1101101 i binärt&lt;br /&gt;
&lt;br /&gt;
// Hårdvaruoptimerad paritetsberäkning&lt;br /&gt;
static inline uint8_t parity(uint8_t x) {&lt;br /&gt;
    return __builtin_parity(x) &amp;amp; 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// KODARE (-e)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void encode(FILE *in, FILE *out) {&lt;br /&gt;
    uint8_t encoder_state = 0;&lt;br /&gt;
    uint8_t out_byte = 0;&lt;br /&gt;
    int bit_count = 0;&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
        // Processa varje bit från MSB till LSB&lt;br /&gt;
        for (int i = 7; i &amp;gt;= 0; i--) {&lt;br /&gt;
            uint8_t bit = (ch &amp;gt;&amp;gt; i) &amp;amp; 1;&lt;br /&gt;
            uint8_t current_word = (encoder_state &amp;lt;&amp;lt; 1) | bit;&lt;br /&gt;
&lt;br /&gt;
            // Beräkna de två paritetsbitarna&lt;br /&gt;
            uint8_t out0 = parity(current_word &amp;amp; POLY_G0);&lt;br /&gt;
            uint8_t out1 = parity(current_word &amp;amp; POLY_G1);&lt;br /&gt;
&lt;br /&gt;
            // Packa bit 0&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out0;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Packa bit 1&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out1;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Uppdatera skiftregistret (behåll K-1 bitar)&lt;br /&gt;
            encoder_state = current_word &amp;amp; (NUM_STATES - 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Töm resterande bitar i sista byten vid behov&lt;br /&gt;
    if (bit_count &amp;gt; 0) {&lt;br /&gt;
        out_byte &amp;lt;&amp;lt;= (8 - bit_count);&lt;br /&gt;
        fputc(out_byte, out);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// AVKODARE (-d)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void decode(FILE *in, FILE *out) {&lt;br /&gt;
    size_t history_capacity = 1024;&lt;br /&gt;
    size_t step = 0;&lt;br /&gt;
&lt;br /&gt;
    uint8_t (*history)[NUM_STATES] = malloc(history_capacity * sizeof(*history));&lt;br /&gt;
    if (!history) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    uint32_t path_metrics[NUM_STATES];&lt;br /&gt;
    uint32_t old_metrics[NUM_STATES];&lt;br /&gt;
    &lt;br /&gt;
    // Initiera felkostnader (tillstånd 0 är garanterad start)&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        path_metrics[i] = 1e6;&lt;br /&gt;
    }&lt;br /&gt;
    path_metrics[0] = 0;&lt;br /&gt;
&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    // 1. Framåtpass (Forward Pass / Add-Compare-Select)&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
        for (int bit_pair = 3; bit_pair &amp;gt;= 0; bit_pair--) {&lt;br /&gt;
            uint8_t b0 = (ch &amp;gt;&amp;gt; (bit_pair * 2 + 1)) &amp;amp; 1;&lt;br /&gt;
            uint8_t b1 = (ch &amp;gt;&amp;gt; (bit_pair * 2)) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            // Expandera historikbufferten dynamiskt om det behövs&lt;br /&gt;
            if (step &amp;gt;= history_capacity) {&lt;br /&gt;
                history_capacity *= 2;&lt;br /&gt;
                uint8_t (*new_history)[NUM_STATES] = realloc(history, history_capacity * sizeof(*history));&lt;br /&gt;
                if (!new_history) {&lt;br /&gt;
                    free(history);&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
                history = new_history;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            memcpy(old_metrics, path_metrics, sizeof(path_metrics));&lt;br /&gt;
&lt;br /&gt;
            // Gå igenom alla möjliga måltillstånd&lt;br /&gt;
            for (int state = 0; state &amp;lt; NUM_STATES; state++) {&lt;br /&gt;
                int prev0 = state &amp;gt;&amp;gt; 1;&lt;br /&gt;
                int prev1 = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
                uint8_t input_bit = state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
                // Väg 0&lt;br /&gt;
                uint8_t word0 = (prev0 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp0_0 = parity(word0 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp0_1 = parity(word0 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost0 = (exp0_0 != b0) + (exp0_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                // Väg 1&lt;br /&gt;
                uint8_t word1 = (prev1 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp1_0 = parity(word1 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp1_1 = parity(word1 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost1 = (exp1_0 != b0) + (exp1_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                uint32_t m0 = old_metrics[prev0] + cost0;&lt;br /&gt;
                uint32_t m1 = old_metrics[prev1] + cost1;&lt;br /&gt;
&lt;br /&gt;
                // Välj den bästa överlevande vägen&lt;br /&gt;
                if (m0 &amp;lt;= m1) {&lt;br /&gt;
                    path_metrics[state] = m0;&lt;br /&gt;
                    history[step][state] = 0;&lt;br /&gt;
                } else {&lt;br /&gt;
                    path_metrics[state] = m1;&lt;br /&gt;
                    history[step][state] = 1;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            step++;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (step == 0) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 2. Hitta bästa sluttillstånd (lägsta metric)&lt;br /&gt;
    int curr_state = 0;&lt;br /&gt;
    uint32_t min_m = path_metrics[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        if (path_metrics[i] &amp;lt; min_m) {&lt;br /&gt;
            min_m = path_metrics[i];&lt;br /&gt;
            curr_state = i;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 3. Bakåtspårning (Traceback)&lt;br /&gt;
    uint8_t *decoded_bits = malloc(step);&lt;br /&gt;
    if (!decoded_bits) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (int s = (int)step - 1; s &amp;gt;= 0; s--) {&lt;br /&gt;
        decoded_bits[s] = curr_state &amp;amp; 1;&lt;br /&gt;
        int decision = history[s][curr_state];&lt;br /&gt;
        int prev0 = curr_state &amp;gt;&amp;gt; 1;&lt;br /&gt;
        &lt;br /&gt;
        if (decision == 0) {&lt;br /&gt;
            curr_state = prev0;&lt;br /&gt;
        } else {&lt;br /&gt;
            curr_state = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 4. Återpacka till bytes och skriv ut till strömmen&lt;br /&gt;
    uint8_t current_byte = 0;&lt;br /&gt;
    for (size_t i = 0; i &amp;lt; step; i++) {&lt;br /&gt;
        current_byte = (current_byte &amp;lt;&amp;lt; 1) | decoded_bits[i];&lt;br /&gt;
        if ((i + 1) % 8 == 0) {&lt;br /&gt;
            fputc(current_byte, out);&lt;br /&gt;
            current_byte = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    free(decoded_bits);&lt;br /&gt;
    free(history);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// INTERNT TEST (-t)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void run_test(void) {&lt;br /&gt;
    printf(&amp;quot;[*] Startar internt test (64 bytes randomiserat data)...\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    uint8_t original_data[64];&lt;br /&gt;
    srand((unsigned int)time(NULL));&lt;br /&gt;
    for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
        original_data[i] = rand() % 256;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    FILE *orig_stream = tmpfile();&lt;br /&gt;
    FILE *encoded_stream = tmpfile();&lt;br /&gt;
    FILE *decoded_stream = tmpfile();&lt;br /&gt;
&lt;br /&gt;
    if (!orig_stream || !encoded_stream || !decoded_stream) {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Kunde inte skapa temporära filer.\n&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fwrite(original_data, 1, 64, orig_stream);&lt;br /&gt;
    rewind(orig_stream);&lt;br /&gt;
&lt;br /&gt;
    encode(orig_stream, encoded_stream);&lt;br /&gt;
    rewind(encoded_stream);&lt;br /&gt;
&lt;br /&gt;
    decode(encoded_stream, decoded_stream);&lt;br /&gt;
    rewind(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    uint8_t final_data[64];&lt;br /&gt;
    size_t read_bytes = fread(final_data, 1, 64, decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    bool success = true;&lt;br /&gt;
    if (read_bytes != 64) {&lt;br /&gt;
        printf(&amp;quot;[FAIL] Avkodade fel antal bytes: %zu (förväntade 64)\n&amp;quot;, read_bytes);&lt;br /&gt;
        success = false;&lt;br /&gt;
    } else {&lt;br /&gt;
        for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
            if (original_data[i] != final_data[i]) {&lt;br /&gt;
                printf(&amp;quot;[FAIL] Diff vid byte %d: Org=0x%02X, Avkodad=0x%02X\n&amp;quot;, i, original_data[i], final_data[i]);&lt;br /&gt;
                success = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fclose(orig_stream);&lt;br /&gt;
    fclose(encoded_stream);&lt;br /&gt;
    fclose(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    if (success) {&lt;br /&gt;
        printf(&amp;quot;[SUCCESS] Testet lyckades! Allt data matchar perfekt.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// HJÄLPTEXT OCH MAIN&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void print_help(const char *prog_name) {&lt;br /&gt;
    printf(&amp;quot;Användning: %s [option]\n&amp;quot;, prog_name);&lt;br /&gt;
    printf(&amp;quot;Optioner:\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -e &amp;lt;infil &amp;gt;utfil   Kodar infil till utfil (Rate 1/2, K=7)\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -d &amp;lt;infil &amp;gt;utfil   Avkodar infil till utfil med Viterbi\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -h                 Visar denna hjälptext\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -t                 Kör internt test med 64 bytes slumpdata\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (strcmp(argv[1], &amp;quot;-e&amp;quot;) == 0) {&lt;br /&gt;
        encode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-d&amp;quot;) == 0) {&lt;br /&gt;
        decode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-h&amp;quot;) == 0) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-t&amp;quot;) == 0) {&lt;br /&gt;
        run_test();&lt;br /&gt;
    } else {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Fel: Okänd parameter &#039;%s&#039;\n&amp;quot;, argv[1]);&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1906</id>
		<title>Viterbi</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1906"/>
		<updated>2026-06-23T17:37:23Z</updated>

		<summary type="html">&lt;p&gt;Anders: /* Modern C */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= Implementation =&lt;br /&gt;
&lt;br /&gt;
== Modern C ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Anders Sikvall 2026 Version 1.0&lt;br /&gt;
 * &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
 * &lt;br /&gt;
 * Implementation i C av en standard Viterbi-algoritm som används bl.a. av &lt;br /&gt;
 * Qualcomm och NASA och är välpublicerad.&lt;br /&gt;
 *&lt;br /&gt;
 * PUBLIC DOMAIN FOR EDUCATIONAL PURPOSES&lt;br /&gt;
 *&lt;br /&gt;
 * Kompilera med gcc -o viterbi viterbi.c&lt;br /&gt;
 *&lt;br /&gt;
 * ANVÄNDNING&lt;br /&gt;
 *         viterbi -e &amp;lt;indatastream &amp;gt;utdatastream   Kodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -d &amp;lt;indatastream &amp;gt;utdatastream   Avkodar &amp;lt;indatastream till &amp;gt;utdatastream&lt;br /&gt;
 *         viterbi -t                               Utför en intern test på 64 randomiserade oktetter&lt;br /&gt;
 *         viterbi -h                               Visar en kort hjälptext&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define K 7&lt;br /&gt;
#define NUM_STATES (1 &amp;lt;&amp;lt; (K - 1))&lt;br /&gt;
&lt;br /&gt;
// Standard NASA/Qualcomm-polynom (Rate 1/2, K=7)&lt;br /&gt;
const uint8_t POLY_G0 = 0x4F; // 1001111 i binärt&lt;br /&gt;
const uint8_t POLY_G1 = 0x6D; // 1101101 i binärt&lt;br /&gt;
&lt;br /&gt;
// Hårdvaruoptimerad paritetsberäkning&lt;br /&gt;
static inline uint8_t parity(uint8_t x) {&lt;br /&gt;
    return __builtin_parity(x) &amp;amp; 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// KODARE (-e)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void encode(FILE *in, FILE *out) {&lt;br /&gt;
    uint8_t encoder_state = 0;&lt;br /&gt;
    uint8_t out_byte = 0;&lt;br /&gt;
    int bit_count = 0;&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
        // Processa varje bit från MSB till LSB&lt;br /&gt;
        for (int i = 7; i &amp;gt;= 0; i--) {&lt;br /&gt;
            uint8_t bit = (ch &amp;gt;&amp;gt; i) &amp;amp; 1;&lt;br /&gt;
            uint8_t current_word = (encoder_state &amp;lt;&amp;lt; 1) | bit;&lt;br /&gt;
&lt;br /&gt;
            // Beräkna de två paritetsbitarna&lt;br /&gt;
            uint8_t out0 = parity(current_word &amp;amp; POLY_G0);&lt;br /&gt;
            uint8_t out1 = parity(current_word &amp;amp; POLY_G1);&lt;br /&gt;
&lt;br /&gt;
            // Packa bit 0&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out0;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Packa bit 1&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out1;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Uppdatera skiftregistret (behåll K-1 bitar)&lt;br /&gt;
            encoder_state = current_word &amp;amp; (NUM_STATES - 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Töm resterande bitar i sista byten vid behov&lt;br /&gt;
    if (bit_count &amp;gt; 0) {&lt;br /&gt;
        out_byte &amp;lt;&amp;lt;= (8 - bit_count);&lt;br /&gt;
        fputc(out_byte, out);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// AVKODARE (-d)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void decode(FILE *in, FILE *out) {&lt;br /&gt;
    size_t history_capacity = 1024;&lt;br /&gt;
    size_t step = 0;&lt;br /&gt;
&lt;br /&gt;
    uint8_t (*history)[NUM_STATES] = malloc(history_capacity * sizeof(*history));&lt;br /&gt;
    if (!history) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    uint32_t path_metrics[NUM_STATES];&lt;br /&gt;
    uint32_t old_metrics[NUM_STATES];&lt;br /&gt;
    &lt;br /&gt;
    // Initiera felkostnader (tillstånd 0 är garanterad start)&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        path_metrics[i] = 1e6;&lt;br /&gt;
    }&lt;br /&gt;
    path_metrics[0] = 0;&lt;br /&gt;
&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    // 1. Framåtpass (Forward Pass / Add-Compare-Select)&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
        for (int bit_pair = 3; bit_pair &amp;gt;= 0; bit_pair--) {&lt;br /&gt;
            uint8_t b0 = (ch &amp;gt;&amp;gt; (bit_pair * 2 + 1)) &amp;amp; 1;&lt;br /&gt;
            uint8_t b1 = (ch &amp;gt;&amp;gt; (bit_pair * 2)) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            // Expandera historikbufferten dynamiskt om det behövs&lt;br /&gt;
            if (step &amp;gt;= history_capacity) {&lt;br /&gt;
                history_capacity *= 2;&lt;br /&gt;
                uint8_t (*new_history)[NUM_STATES] = realloc(history, history_capacity * sizeof(*history));&lt;br /&gt;
                if (!new_history) {&lt;br /&gt;
                    free(history);&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
                history = new_history;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            memcpy(old_metrics, path_metrics, sizeof(path_metrics));&lt;br /&gt;
&lt;br /&gt;
            // Gå igenom alla möjliga måltillstånd&lt;br /&gt;
            for (int state = 0; state &amp;lt; NUM_STATES; state++) {&lt;br /&gt;
                int prev0 = state &amp;gt;&amp;gt; 1;&lt;br /&gt;
                int prev1 = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
                uint8_t input_bit = state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
                // Väg 0&lt;br /&gt;
                uint8_t word0 = (prev0 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp0_0 = parity(word0 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp0_1 = parity(word0 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost0 = (exp0_0 != b0) + (exp0_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                // Väg 1&lt;br /&gt;
                uint8_t word1 = (prev1 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp1_0 = parity(word1 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp1_1 = parity(word1 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost1 = (exp1_0 != b0) + (exp1_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                uint32_t m0 = old_metrics[prev0] + cost0;&lt;br /&gt;
                uint32_t m1 = old_metrics[prev1] + cost1;&lt;br /&gt;
&lt;br /&gt;
                // Välj den bästa överlevande vägen&lt;br /&gt;
                if (m0 &amp;lt;= m1) {&lt;br /&gt;
                    path_metrics[state] = m0;&lt;br /&gt;
                    history[step][state] = 0;&lt;br /&gt;
                } else {&lt;br /&gt;
                    path_metrics[state] = m1;&lt;br /&gt;
                    history[step][state] = 1;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            step++;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (step == 0) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 2. Hitta bästa sluttillstånd (lägsta metric)&lt;br /&gt;
    int curr_state = 0;&lt;br /&gt;
    uint32_t min_m = path_metrics[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        if (path_metrics[i] &amp;lt; min_m) {&lt;br /&gt;
            min_m = path_metrics[i];&lt;br /&gt;
            curr_state = i;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 3. Bakåtspårning (Traceback)&lt;br /&gt;
    uint8_t *decoded_bits = malloc(step);&lt;br /&gt;
    if (!decoded_bits) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (int s = (int)step - 1; s &amp;gt;= 0; s--) {&lt;br /&gt;
        decoded_bits[s] = curr_state &amp;amp; 1;&lt;br /&gt;
        int decision = history[s][curr_state];&lt;br /&gt;
        int prev0 = curr_state &amp;gt;&amp;gt; 1;&lt;br /&gt;
        &lt;br /&gt;
        if (decision == 0) {&lt;br /&gt;
            curr_state = prev0;&lt;br /&gt;
        } else {&lt;br /&gt;
            curr_state = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 4. Återpacka till bytes och skriv ut till strömmen&lt;br /&gt;
    uint8_t current_byte = 0;&lt;br /&gt;
    for (size_t i = 0; i &amp;lt; step; i++) {&lt;br /&gt;
        current_byte = (current_byte &amp;lt;&amp;lt; 1) | decoded_bits[i];&lt;br /&gt;
        if ((i + 1) % 8 == 0) {&lt;br /&gt;
            fputc(current_byte, out);&lt;br /&gt;
            current_byte = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    free(decoded_bits);&lt;br /&gt;
    free(history);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// INTERNT TEST (-t)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void run_test(void) {&lt;br /&gt;
    printf(&amp;quot;[*] Startar internt test (64 bytes randomiserat data)...\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    uint8_t original_data[64];&lt;br /&gt;
    srand((unsigned int)time(NULL));&lt;br /&gt;
    for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
        original_data[i] = rand() % 256;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    FILE *orig_stream = tmpfile();&lt;br /&gt;
    FILE *encoded_stream = tmpfile();&lt;br /&gt;
    FILE *decoded_stream = tmpfile();&lt;br /&gt;
&lt;br /&gt;
    if (!orig_stream || !encoded_stream || !decoded_stream) {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Kunde inte skapa temporära filer.\n&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fwrite(original_data, 1, 64, orig_stream);&lt;br /&gt;
    rewind(orig_stream);&lt;br /&gt;
&lt;br /&gt;
    encode(orig_stream, encoded_stream);&lt;br /&gt;
    rewind(encoded_stream);&lt;br /&gt;
&lt;br /&gt;
    decode(encoded_stream, decoded_stream);&lt;br /&gt;
    rewind(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    uint8_t final_data[64];&lt;br /&gt;
    size_t read_bytes = fread(final_data, 1, 64, decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    bool success = true;&lt;br /&gt;
    if (read_bytes != 64) {&lt;br /&gt;
        printf(&amp;quot;[FAIL] Avkodade fel antal bytes: %zu (förväntade 64)\n&amp;quot;, read_bytes);&lt;br /&gt;
        success = false;&lt;br /&gt;
    } else {&lt;br /&gt;
        for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
            if (original_data[i] != final_data[i]) {&lt;br /&gt;
                printf(&amp;quot;[FAIL] Diff vid byte %d: Org=0x%02X, Avkodad=0x%02X\n&amp;quot;, i, original_data[i], final_data[i]);&lt;br /&gt;
                success = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fclose(orig_stream);&lt;br /&gt;
    fclose(encoded_stream);&lt;br /&gt;
    fclose(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    if (success) {&lt;br /&gt;
        printf(&amp;quot;[SUCCESS] Testet lyckades! Allt data matchar perfekt.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// HJÄLPTEXT OCH MAIN&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void print_help(const char *prog_name) {&lt;br /&gt;
    printf(&amp;quot;Användning: %s [option]\n&amp;quot;, prog_name);&lt;br /&gt;
    printf(&amp;quot;Optioner:\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -e &amp;lt;infil &amp;gt;utfil   Kodar infil till utfil (Rate 1/2, K=7)\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -d &amp;lt;infil &amp;gt;utfil   Avkodar infil till utfil med Viterbi\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -h                 Visar denna hjälptext\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -t                 Kör internt test med 64 bytes slumpdata\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (strcmp(argv[1], &amp;quot;-e&amp;quot;) == 0) {&lt;br /&gt;
        encode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-d&amp;quot;) == 0) {&lt;br /&gt;
        decode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-h&amp;quot;) == 0) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-t&amp;quot;) == 0) {&lt;br /&gt;
        run_test();&lt;br /&gt;
    } else {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Fel: Okänd parameter &#039;%s&#039;\n&amp;quot;, argv[1]);&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1905</id>
		<title>Viterbi</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Viterbi&amp;diff=1905"/>
		<updated>2026-06-23T17:34:12Z</updated>

		<summary type="html">&lt;p&gt;Anders: Skapade sidan med &amp;#039; = Implementation =  == Modern C ==  &amp;lt;pre&amp;gt;  /*  * Anders Sikvall 2026 Version 1.0  * &amp;lt;anders@sikvall.se&amp;gt;  *   * Implementation i C av en standard Viterbi-algoritm som används bl.a. av   * Qualcomm och NASA och är välpublicerad.  *  * PUBLIC DOMAIN FOR EDUCATIONAL PURPOSES  */  #include &amp;lt;stdio.h&amp;gt; #include &amp;lt;stdlib.h&amp;gt; #include &amp;lt;string.h&amp;gt; #include &amp;lt;stdbool.h&amp;gt; #include &amp;lt;stdint.h&amp;gt; #include &amp;lt;time.h&amp;gt;  #define K 7 #define NUM_STATES (1 &amp;lt;&amp;lt; (K - 1))  // Standard NASA/Qualcomm-po...&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= Implementation =&lt;br /&gt;
&lt;br /&gt;
== Modern C ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Anders Sikvall 2026 Version 1.0&lt;br /&gt;
 * &amp;lt;anders@sikvall.se&amp;gt;&lt;br /&gt;
 * &lt;br /&gt;
 * Implementation i C av en standard Viterbi-algoritm som används bl.a. av &lt;br /&gt;
 * Qualcomm och NASA och är välpublicerad.&lt;br /&gt;
 *&lt;br /&gt;
 * PUBLIC DOMAIN FOR EDUCATIONAL PURPOSES&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define K 7&lt;br /&gt;
#define NUM_STATES (1 &amp;lt;&amp;lt; (K - 1))&lt;br /&gt;
&lt;br /&gt;
// Standard NASA/Qualcomm-polynom (Rate 1/2, K=7)&lt;br /&gt;
const uint8_t POLY_G0 = 0x4F; // 1001111 i binärt&lt;br /&gt;
const uint8_t POLY_G1 = 0x6D; // 1101101 i binärt&lt;br /&gt;
&lt;br /&gt;
// Hårdvaruoptimerad paritetsberäkning&lt;br /&gt;
static inline uint8_t parity(uint8_t x) {&lt;br /&gt;
    return __builtin_parity(x) &amp;amp; 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// KODARE (-e)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void encode(FILE *in, FILE *out) {&lt;br /&gt;
    uint8_t encoder_state = 0;&lt;br /&gt;
    uint8_t out_byte = 0;&lt;br /&gt;
    int bit_count = 0;&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
        // Processa varje bit från MSB till LSB&lt;br /&gt;
        for (int i = 7; i &amp;gt;= 0; i--) {&lt;br /&gt;
            uint8_t bit = (ch &amp;gt;&amp;gt; i) &amp;amp; 1;&lt;br /&gt;
            uint8_t current_word = (encoder_state &amp;lt;&amp;lt; 1) | bit;&lt;br /&gt;
&lt;br /&gt;
            // Beräkna de två paritetsbitarna&lt;br /&gt;
            uint8_t out0 = parity(current_word &amp;amp; POLY_G0);&lt;br /&gt;
            uint8_t out1 = parity(current_word &amp;amp; POLY_G1);&lt;br /&gt;
&lt;br /&gt;
            // Packa bit 0&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out0;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Packa bit 1&lt;br /&gt;
            out_byte = (out_byte &amp;lt;&amp;lt; 1) | out1;&lt;br /&gt;
            bit_count++;&lt;br /&gt;
            if (bit_count == 8) {&lt;br /&gt;
                fputc(out_byte, out);&lt;br /&gt;
                out_byte = 0;&lt;br /&gt;
                bit_count = 0;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Uppdatera skiftregistret (behåll K-1 bitar)&lt;br /&gt;
            encoder_state = current_word &amp;amp; (NUM_STATES - 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Töm resterande bitar i sista byten vid behov&lt;br /&gt;
    if (bit_count &amp;gt; 0) {&lt;br /&gt;
        out_byte &amp;lt;&amp;lt;= (8 - bit_count);&lt;br /&gt;
        fputc(out_byte, out);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// AVKODARE (-d)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void decode(FILE *in, FILE *out) {&lt;br /&gt;
    size_t history_capacity = 1024;&lt;br /&gt;
    size_t step = 0;&lt;br /&gt;
&lt;br /&gt;
    uint8_t (*history)[NUM_STATES] = malloc(history_capacity * sizeof(*history));&lt;br /&gt;
    if (!history) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    uint32_t path_metrics[NUM_STATES];&lt;br /&gt;
    uint32_t old_metrics[NUM_STATES];&lt;br /&gt;
    &lt;br /&gt;
    // Initiera felkostnader (tillstånd 0 är garanterad start)&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        path_metrics[i] = 1e6;&lt;br /&gt;
    }&lt;br /&gt;
    path_metrics[0] = 0;&lt;br /&gt;
&lt;br /&gt;
    int ch;&lt;br /&gt;
&lt;br /&gt;
    // 1. Framåtpass (Forward Pass / Add-Compare-Select)&lt;br /&gt;
    while ((ch = fgetc(in)) != EOF) {&lt;br /&gt;
        for (int bit_pair = 3; bit_pair &amp;gt;= 0; bit_pair--) {&lt;br /&gt;
            uint8_t b0 = (ch &amp;gt;&amp;gt; (bit_pair * 2 + 1)) &amp;amp; 1;&lt;br /&gt;
            uint8_t b1 = (ch &amp;gt;&amp;gt; (bit_pair * 2)) &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
            // Expandera historikbufferten dynamiskt om det behövs&lt;br /&gt;
            if (step &amp;gt;= history_capacity) {&lt;br /&gt;
                history_capacity *= 2;&lt;br /&gt;
                uint8_t (*new_history)[NUM_STATES] = realloc(history, history_capacity * sizeof(*history));&lt;br /&gt;
                if (!new_history) {&lt;br /&gt;
                    free(history);&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
                history = new_history;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            memcpy(old_metrics, path_metrics, sizeof(path_metrics));&lt;br /&gt;
&lt;br /&gt;
            // Gå igenom alla möjliga måltillstånd&lt;br /&gt;
            for (int state = 0; state &amp;lt; NUM_STATES; state++) {&lt;br /&gt;
                int prev0 = state &amp;gt;&amp;gt; 1;&lt;br /&gt;
                int prev1 = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
                uint8_t input_bit = state &amp;amp; 1;&lt;br /&gt;
&lt;br /&gt;
                // Väg 0&lt;br /&gt;
                uint8_t word0 = (prev0 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp0_0 = parity(word0 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp0_1 = parity(word0 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost0 = (exp0_0 != b0) + (exp0_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                // Väg 1&lt;br /&gt;
                uint8_t word1 = (prev1 &amp;lt;&amp;lt; 1) | input_bit;&lt;br /&gt;
                uint8_t exp1_0 = parity(word1 &amp;amp; POLY_G0);&lt;br /&gt;
                uint8_t exp1_1 = parity(word1 &amp;amp; POLY_G1);&lt;br /&gt;
                uint32_t cost1 = (exp1_0 != b0) + (exp1_1 != b1);&lt;br /&gt;
&lt;br /&gt;
                uint32_t m0 = old_metrics[prev0] + cost0;&lt;br /&gt;
                uint32_t m1 = old_metrics[prev1] + cost1;&lt;br /&gt;
&lt;br /&gt;
                // Välj den bästa överlevande vägen&lt;br /&gt;
                if (m0 &amp;lt;= m1) {&lt;br /&gt;
                    path_metrics[state] = m0;&lt;br /&gt;
                    history[step][state] = 0;&lt;br /&gt;
                } else {&lt;br /&gt;
                    path_metrics[state] = m1;&lt;br /&gt;
                    history[step][state] = 1;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            step++;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (step == 0) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 2. Hitta bästa sluttillstånd (lägsta metric)&lt;br /&gt;
    int curr_state = 0;&lt;br /&gt;
    uint32_t min_m = path_metrics[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; NUM_STATES; i++) {&lt;br /&gt;
        if (path_metrics[i] &amp;lt; min_m) {&lt;br /&gt;
            min_m = path_metrics[i];&lt;br /&gt;
            curr_state = i;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 3. Bakåtspårning (Traceback)&lt;br /&gt;
    uint8_t *decoded_bits = malloc(step);&lt;br /&gt;
    if (!decoded_bits) {&lt;br /&gt;
        free(history);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (int s = (int)step - 1; s &amp;gt;= 0; s--) {&lt;br /&gt;
        decoded_bits[s] = curr_state &amp;amp; 1;&lt;br /&gt;
        int decision = history[s][curr_state];&lt;br /&gt;
        int prev0 = curr_state &amp;gt;&amp;gt; 1;&lt;br /&gt;
        &lt;br /&gt;
        if (decision == 0) {&lt;br /&gt;
            curr_state = prev0;&lt;br /&gt;
        } else {&lt;br /&gt;
            curr_state = prev0 | (NUM_STATES &amp;gt;&amp;gt; 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 4. Återpacka till bytes och skriv ut till strömmen&lt;br /&gt;
    uint8_t current_byte = 0;&lt;br /&gt;
    for (size_t i = 0; i &amp;lt; step; i++) {&lt;br /&gt;
        current_byte = (current_byte &amp;lt;&amp;lt; 1) | decoded_bits[i];&lt;br /&gt;
        if ((i + 1) % 8 == 0) {&lt;br /&gt;
            fputc(current_byte, out);&lt;br /&gt;
            current_byte = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    free(decoded_bits);&lt;br /&gt;
    free(history);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// INTERNT TEST (-t)&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void run_test(void) {&lt;br /&gt;
    printf(&amp;quot;[*] Startar internt test (64 bytes randomiserat data)...\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    uint8_t original_data[64];&lt;br /&gt;
    srand((unsigned int)time(NULL));&lt;br /&gt;
    for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
        original_data[i] = rand() % 256;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    FILE *orig_stream = tmpfile();&lt;br /&gt;
    FILE *encoded_stream = tmpfile();&lt;br /&gt;
    FILE *decoded_stream = tmpfile();&lt;br /&gt;
&lt;br /&gt;
    if (!orig_stream || !encoded_stream || !decoded_stream) {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Kunde inte skapa temporära filer.\n&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fwrite(original_data, 1, 64, orig_stream);&lt;br /&gt;
    rewind(orig_stream);&lt;br /&gt;
&lt;br /&gt;
    encode(orig_stream, encoded_stream);&lt;br /&gt;
    rewind(encoded_stream);&lt;br /&gt;
&lt;br /&gt;
    decode(encoded_stream, decoded_stream);&lt;br /&gt;
    rewind(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    uint8_t final_data[64];&lt;br /&gt;
    size_t read_bytes = fread(final_data, 1, 64, decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    bool success = true;&lt;br /&gt;
    if (read_bytes != 64) {&lt;br /&gt;
        printf(&amp;quot;[FAIL] Avkodade fel antal bytes: %zu (förväntade 64)\n&amp;quot;, read_bytes);&lt;br /&gt;
        success = false;&lt;br /&gt;
    } else {&lt;br /&gt;
        for (int i = 0; i &amp;lt; 64; i++) {&lt;br /&gt;
            if (original_data[i] != final_data[i]) {&lt;br /&gt;
                printf(&amp;quot;[FAIL] Diff vid byte %d: Org=0x%02X, Avkodad=0x%02X\n&amp;quot;, i, original_data[i], final_data[i]);&lt;br /&gt;
                success = false;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fclose(orig_stream);&lt;br /&gt;
    fclose(encoded_stream);&lt;br /&gt;
    fclose(decoded_stream);&lt;br /&gt;
&lt;br /&gt;
    if (success) {&lt;br /&gt;
        printf(&amp;quot;[SUCCESS] Testet lyckades! Allt data matchar perfekt.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ============================================================================&lt;br /&gt;
// HJÄLPTEXT OCH MAIN&lt;br /&gt;
// ============================================================================&lt;br /&gt;
void print_help(const char *prog_name) {&lt;br /&gt;
    printf(&amp;quot;Användning: %s [option]\n&amp;quot;, prog_name);&lt;br /&gt;
    printf(&amp;quot;Optioner:\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -e &amp;lt;infil &amp;gt;utfil   Kodar infil till utfil (Rate 1/2, K=7)\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -d &amp;lt;infil &amp;gt;utfil   Avkodar infil till utfil med Viterbi\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -h                 Visar denna hjälptext\n&amp;quot;);&lt;br /&gt;
    printf(&amp;quot;  -t                 Kör internt test med 64 bytes slumpdata\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (strcmp(argv[1], &amp;quot;-e&amp;quot;) == 0) {&lt;br /&gt;
        encode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-d&amp;quot;) == 0) {&lt;br /&gt;
        decode(stdin, stdout);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-h&amp;quot;) == 0) {&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;-t&amp;quot;) == 0) {&lt;br /&gt;
        run_test();&lt;br /&gt;
    } else {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Fel: Okänd parameter &#039;%s&#039;\n&amp;quot;, argv[1]);&lt;br /&gt;
        print_help(argv[0]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Huvudsida&amp;diff=1904</id>
		<title>Huvudsida</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Huvudsida&amp;diff=1904"/>
		<updated>2026-06-23T17:32:36Z</updated>

		<summary type="html">&lt;p&gt;Anders: /* Programmering */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Om mig = &lt;br /&gt;
&lt;br /&gt;
Ansvarig för denna Wiki är Anders Sikvall&lt;br /&gt;
[mailto:anders@sikvall.se anders@sikvall.se] (Proton mail krypterad)&lt;br /&gt;
&lt;br /&gt;
Du kan hitta min PGP/GPG-nyckel på denna URL:&lt;br /&gt;
[https://sikvall.se/pgp/anders@sikvall.se https://sikvall.se/pgp/anders@sikvall.se]&lt;br /&gt;
&lt;br /&gt;
Min hemsida finns på: &lt;br /&gt;
[https://sikvall.se/ https://sikvall.se/]&lt;br /&gt;
&lt;br /&gt;
= Specialsidor =&lt;br /&gt;
&lt;br /&gt;
Specialsidor är länkar till sidor som omfattar meta-information.&lt;br /&gt;
&lt;br /&gt;
* [[:Special:Categories|Alla kategorier]]&lt;br /&gt;
* [[:Special:AllPages|Alla sidor]]&lt;br /&gt;
* [[dBµV/m]]&lt;br /&gt;
&lt;br /&gt;
= Särskilt utvalda sidor =&lt;br /&gt;
&lt;br /&gt;
== Krypto ==&lt;br /&gt;
&lt;br /&gt;
* [[GPG]]&lt;br /&gt;
&lt;br /&gt;
== Programmering ==&lt;br /&gt;
&lt;br /&gt;
* [[Fisher-Yates Shuffle]]&lt;br /&gt;
* [[Base64]] [[Base32]] [[Base24]]&lt;br /&gt;
* [[Racket]]&lt;br /&gt;
* [[Viterbi]]&lt;br /&gt;
&lt;br /&gt;
== Elektronik ==&lt;br /&gt;
&lt;br /&gt;
* [[Peukerts lag]] - [[Blyackumulator laddningstabell]]&lt;br /&gt;
* [[Pi-dämpare]]&lt;br /&gt;
&lt;br /&gt;
== Radiorelaterat ==&lt;br /&gt;
&lt;br /&gt;
* [[Amatörradio]]&lt;br /&gt;
* [[Jaktradio 155 MHz]] - [[PMR446]] - [[KDR 444]]&lt;br /&gt;
* [[Q-koden]] - [[Morsealfabetet]] - [[Svenska bokstaveringsalfabetet]]&lt;br /&gt;
* [[AM]] - [[FM]] - [[SSB]] - [[CW]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Linux ==&lt;br /&gt;
&lt;br /&gt;
* [[Diskimage i Linux]]&lt;br /&gt;
* [[Git]]&lt;br /&gt;
* [[Temporär download]]&lt;br /&gt;
* [[Swapfil i BTRFS]]&lt;br /&gt;
* [[Automatiskt tömma soptunnan]]&lt;br /&gt;
&lt;br /&gt;
== LaTeX Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* [[Brevmall]]&lt;br /&gt;
* [[Svensk stilmall]]&lt;br /&gt;
* [[Tips och Tricks i LaTeX]]&lt;br /&gt;
&lt;br /&gt;
== Friluftsliv ==&lt;br /&gt;
* [[:category:Pannlampor|Pannlampor]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L_f&amp;lt;/math&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/EFRW&amp;diff=1903</id>
		<title>Racket/EFRW</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/EFRW&amp;diff=1903"/>
		<updated>2026-06-22T08:58:41Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;; Täpp-Anders Sikvall, SM5UEI&lt;br /&gt;
;; &amp;lt;sm5uei@sikvall.se&amp;gt;&lt;br /&gt;
;;&lt;br /&gt;
;; Public Domain&lt;br /&gt;
;;&lt;br /&gt;
;; Koden den är fri att använda för den som vill och återanvända &lt;br /&gt;
;; och pyssla vidare med den som du vill. Jag blir glad om &lt;br /&gt;
;; jag omnämns i vidareutvecklingar och gör du förbättringar så&lt;br /&gt;
;; skicka de gärna till mig.&lt;br /&gt;
;;&lt;br /&gt;
;; =====================================================&lt;br /&gt;
;; Ändmatad antenn – längdintervall + vilka band som träffas&lt;br /&gt;
;; =====================================================&lt;br /&gt;
&lt;br /&gt;
(define c 299.792458)   ; m×MHz&lt;br /&gt;
&lt;br /&gt;
;; Svenska band, kommentera bort de band du inte är intresserad av att köra!&lt;br /&gt;
(define bands&lt;br /&gt;
  &#039;((1.810  2.000   &amp;quot;160m&amp;quot;)&lt;br /&gt;
    (3.500  3.800   &amp;quot;80m&amp;quot;)&lt;br /&gt;
    (5.3515 5.3666  &amp;quot;60m&amp;quot;)&lt;br /&gt;
    (7.000  7.200   &amp;quot;40m&amp;quot;)&lt;br /&gt;
;    (10.100 10.150  &amp;quot;30m&amp;quot;)&lt;br /&gt;
    (14.000 14.350  &amp;quot;20m&amp;quot;)&lt;br /&gt;
    (18.068 18.168  &amp;quot;17m&amp;quot;)&lt;br /&gt;
    (21.000 21.450  &amp;quot;15m&amp;quot;)&lt;br /&gt;
    (24.890 24.990  &amp;quot;12m&amp;quot;)&lt;br /&gt;
    (28.000 29.700  &amp;quot;10m&amp;quot;)&lt;br /&gt;
    (50.000 52.000  &amp;quot;6m&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
;; Returnerar lista med band som L är dåliga för en given längd&lt;br /&gt;
(define (bad-bands L tol)&lt;br /&gt;
  (filter-map&lt;br /&gt;
   (λ (band)&lt;br /&gt;
     (define fmin (first band))&lt;br /&gt;
     (define fmax (second band))&lt;br /&gt;
     (define name (third band))&lt;br /&gt;
     (define fmin-forb (* fmin (- 1 tol)))&lt;br /&gt;
     (define fmax-forb (* fmax (+ 1 tol)))&lt;br /&gt;
     (for/or ([n (in-range 1 400)])&lt;br /&gt;
       (define f-res (/ (* n c) (* 2.0 L)))&lt;br /&gt;
       (and (&amp;gt;= f-res fmin-forb) (&amp;lt;= f-res fmax-forb)&lt;br /&gt;
            name)))&lt;br /&gt;
   bands))&lt;br /&gt;
&lt;br /&gt;
(define (bad-length? L tol)&lt;br /&gt;
  (not (null? (bad-bands L tol))))&lt;br /&gt;
&lt;br /&gt;
(define (good-lengths min-L max-L step tol)&lt;br /&gt;
  (for/list ([L (in-range min-L (+ max-L 0.0001) step)]&lt;br /&gt;
             #:when (not (bad-length? L tol)))&lt;br /&gt;
    L))&lt;br /&gt;
&lt;br /&gt;
(define (group-into-ranges goods step)&lt;br /&gt;
  (if (null? goods)&lt;br /&gt;
      &#039;()&lt;br /&gt;
      (let loop ([lst (sort goods &amp;lt;)]&lt;br /&gt;
                 [ranges &#039;()]&lt;br /&gt;
                 [start (first goods)]&lt;br /&gt;
                 [prev (first goods)])&lt;br /&gt;
        (cond&lt;br /&gt;
          [(null? lst) (reverse (cons (list start prev) ranges))]&lt;br /&gt;
          [else&lt;br /&gt;
           (define current (first lst))&lt;br /&gt;
           (if (&amp;lt;= (- current prev) (+ step 0.0001))&lt;br /&gt;
               (loop (rest lst) ranges start current)&lt;br /&gt;
               (loop (rest lst)&lt;br /&gt;
                     (cons (list start prev) ranges)&lt;br /&gt;
                     current&lt;br /&gt;
                     current))]))))&lt;br /&gt;
&lt;br /&gt;
(define (print-range r vf end-corr)&lt;br /&gt;
  (define elec-start (first r))&lt;br /&gt;
  (define elec-end   (second r))&lt;br /&gt;
  (define phys-start (* elec-start vf end-corr))&lt;br /&gt;
  (define phys-end   (* elec-end   vf end-corr))&lt;br /&gt;
  (define s1 (~r elec-start #:precision 1))&lt;br /&gt;
  (define s2 (~r elec-end   #:precision 1))&lt;br /&gt;
  (define p1 (~r phys-start #:precision 1))&lt;br /&gt;
  (define p2 (~r phys-end   #:precision 1))&lt;br /&gt;
  (if (equal? s1 s2)&lt;br /&gt;
      (printf &amp;quot;~a m (elektrisk)  →  ~a m (fysisk)\n&amp;quot; s1 p1)&lt;br /&gt;
      (printf &amp;quot;~a–~a m (elektrisk)  →  ~a–~a m (fysisk)\n&amp;quot; s1 s2 p1 p2)))&lt;br /&gt;
&lt;br /&gt;
;; ====================== HUVUDFUNKTION ======================&lt;br /&gt;
(define (print-suggestions&lt;br /&gt;
         #:min-L     [min-L     10.0]&lt;br /&gt;
         #:max-L     [max-L     70.0]&lt;br /&gt;
         #:step      [step      0.01]&lt;br /&gt;
         #:tol       [tol       0.03]&lt;br /&gt;
         #:vf        [vf        0.96]&lt;br /&gt;
         #:end-corr  [end-corr  0.97])&lt;br /&gt;
  &lt;br /&gt;
  (define goods (good-lengths min-L max-L step tol))&lt;br /&gt;
  (define ranges (group-into-ranges goods step))&lt;br /&gt;
&lt;br /&gt;
  (printf &amp;quot;=== Bra längdintervall för ändmatad antenn ===\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;Intervall: ~a–~a m   Steg: ~a m   Tolerans: ~a%\n&amp;quot; &lt;br /&gt;
          min-L max-L step (* tol 100))&lt;br /&gt;
  (printf &amp;quot;VF: ~a   End-effect: ~a (~a% förkortning)\n\n&amp;quot;&lt;br /&gt;
          vf end-corr (round (* (- 1 end-corr) 100)))&lt;br /&gt;
&lt;br /&gt;
  (if (null? ranges)&lt;br /&gt;
      (printf &amp;quot;Inga bra längder hittades.\n&amp;quot;)&lt;br /&gt;
      (begin&lt;br /&gt;
        (for ([r ranges])&lt;br /&gt;
          (print-range r vf end-corr))&lt;br /&gt;
        (printf &amp;quot;\nTotalt ~a bra intervall (~a punkter).\n&amp;quot; &lt;br /&gt;
                (length ranges) (length goods))))&lt;br /&gt;
&lt;br /&gt;
  (printf &amp;quot;\n&amp;quot;))&lt;br /&gt;
  &lt;br /&gt;
;; =====================================================&lt;br /&gt;
;; KÖR PROGRAMMET&lt;br /&gt;
;; =====================================================&lt;br /&gt;
&lt;br /&gt;
(print-suggestions &lt;br /&gt;
  #:min-L 7        ;; Kortaste antenntråden att kika på&lt;br /&gt;
  #:max-L 50       ;; Längsta antenntråden att kika på&lt;br /&gt;
  #:step 0.01      ;; Steglängd i meter&lt;br /&gt;
  #:tol 0.03       ;; Hur nära resonans tillåter vi (3% = 0.03)&lt;br /&gt;
  #:vf 0.96        ;; Hastighetsfaktorn för plastad koppartråd&lt;br /&gt;
  #:end-corr 0.97  ;; Ändkorrigerinsfaktor&lt;br /&gt;
  )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/EFRW&amp;diff=1902</id>
		<title>Racket/EFRW</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/EFRW&amp;diff=1902"/>
		<updated>2026-06-21T21:28:59Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;; Denna kod är skriven av Täpp-Anders Sikvall, SM5UEI&lt;br /&gt;
;; den är fri att använda för den som vill och återanvända &lt;br /&gt;
;; och pyssla vidare med den som du vill. Jag blir glad om &lt;br /&gt;
;; jag omnämns i vidareutvecklingar och du kan gärna skicka &lt;br /&gt;
;; den till &amp;lt;anders@sikvall.se&amp;gt; om du gjort förbättringar.&lt;br /&gt;
;;&lt;br /&gt;
;; =====================================================&lt;br /&gt;
;; Ändmatad antenn – längdintervall + vilka band som träffas&lt;br /&gt;
;; =====================================================&lt;br /&gt;
&lt;br /&gt;
(define c 299.792458)   ; m×MHz&lt;br /&gt;
&lt;br /&gt;
;; Svenska band, kommentera bort de band du inte är intresserad av att köra!&lt;br /&gt;
(define bands&lt;br /&gt;
  &#039;((1.810  2.000   &amp;quot;160m&amp;quot;)&lt;br /&gt;
    (3.500  3.800   &amp;quot;80m&amp;quot;)&lt;br /&gt;
    (5.3515 5.3666  &amp;quot;60m&amp;quot;)&lt;br /&gt;
    (7.000  7.200   &amp;quot;40m&amp;quot;)&lt;br /&gt;
;    (10.100 10.150  &amp;quot;30m&amp;quot;)&lt;br /&gt;
    (14.000 14.350  &amp;quot;20m&amp;quot;)&lt;br /&gt;
    (18.068 18.168  &amp;quot;17m&amp;quot;)&lt;br /&gt;
    (21.000 21.450  &amp;quot;15m&amp;quot;)&lt;br /&gt;
    (24.890 24.990  &amp;quot;12m&amp;quot;)&lt;br /&gt;
    (28.000 29.700  &amp;quot;10m&amp;quot;)&lt;br /&gt;
    (50.000 52.000  &amp;quot;6m&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
;; Returnerar lista med band som L är dåliga för en given längd&lt;br /&gt;
(define (bad-bands L tol)&lt;br /&gt;
  (filter-map&lt;br /&gt;
   (λ (band)&lt;br /&gt;
     (define fmin (first band))&lt;br /&gt;
     (define fmax (second band))&lt;br /&gt;
     (define name (third band))&lt;br /&gt;
     (define fmin-forb (* fmin (- 1 tol)))&lt;br /&gt;
     (define fmax-forb (* fmax (+ 1 tol)))&lt;br /&gt;
     (for/or ([n (in-range 1 400)])&lt;br /&gt;
       (define f-res (/ (* n c) (* 2.0 L)))&lt;br /&gt;
       (and (&amp;gt;= f-res fmin-forb) (&amp;lt;= f-res fmax-forb)&lt;br /&gt;
            name)))&lt;br /&gt;
   bands))&lt;br /&gt;
&lt;br /&gt;
(define (bad-length? L tol)&lt;br /&gt;
  (not (null? (bad-bands L tol))))&lt;br /&gt;
&lt;br /&gt;
(define (good-lengths min-L max-L step tol)&lt;br /&gt;
  (for/list ([L (in-range min-L (+ max-L 0.0001) step)]&lt;br /&gt;
             #:when (not (bad-length? L tol)))&lt;br /&gt;
    L))&lt;br /&gt;
&lt;br /&gt;
(define (group-into-ranges goods step)&lt;br /&gt;
  (if (null? goods)&lt;br /&gt;
      &#039;()&lt;br /&gt;
      (let loop ([lst (sort goods &amp;lt;)]&lt;br /&gt;
                 [ranges &#039;()]&lt;br /&gt;
                 [start (first goods)]&lt;br /&gt;
                 [prev (first goods)])&lt;br /&gt;
        (cond&lt;br /&gt;
          [(null? lst) (reverse (cons (list start prev) ranges))]&lt;br /&gt;
          [else&lt;br /&gt;
           (define current (first lst))&lt;br /&gt;
           (if (&amp;lt;= (- current prev) (+ step 0.0001))&lt;br /&gt;
               (loop (rest lst) ranges start current)&lt;br /&gt;
               (loop (rest lst)&lt;br /&gt;
                     (cons (list start prev) ranges)&lt;br /&gt;
                     current&lt;br /&gt;
                     current))]))))&lt;br /&gt;
&lt;br /&gt;
(define (print-range r vf end-corr)&lt;br /&gt;
  (define elec-start (first r))&lt;br /&gt;
  (define elec-end   (second r))&lt;br /&gt;
  (define phys-start (* elec-start vf end-corr))&lt;br /&gt;
  (define phys-end   (* elec-end   vf end-corr))&lt;br /&gt;
  (define s1 (~r elec-start #:precision 1))&lt;br /&gt;
  (define s2 (~r elec-end   #:precision 1))&lt;br /&gt;
  (define p1 (~r phys-start #:precision 1))&lt;br /&gt;
  (define p2 (~r phys-end   #:precision 1))&lt;br /&gt;
  (if (equal? s1 s2)&lt;br /&gt;
      (printf &amp;quot;~a m (elektrisk)  →  ~a m (fysisk)\n&amp;quot; s1 p1)&lt;br /&gt;
      (printf &amp;quot;~a–~a m (elektrisk)  →  ~a–~a m (fysisk)\n&amp;quot; s1 s2 p1 p2)))&lt;br /&gt;
&lt;br /&gt;
;; ====================== HUVUDFUNKTION ======================&lt;br /&gt;
(define (print-suggestions&lt;br /&gt;
         #:min-L     [min-L     10.0]&lt;br /&gt;
         #:max-L     [max-L     70.0]&lt;br /&gt;
         #:step      [step      0.01]&lt;br /&gt;
         #:tol       [tol       0.03]&lt;br /&gt;
         #:vf        [vf        0.96]&lt;br /&gt;
         #:end-corr  [end-corr  0.97])&lt;br /&gt;
  &lt;br /&gt;
  (define goods (good-lengths min-L max-L step tol))&lt;br /&gt;
  (define ranges (group-into-ranges goods step))&lt;br /&gt;
&lt;br /&gt;
  (printf &amp;quot;=== Bra längdintervall för ändmatad antenn ===\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;Intervall: ~a–~a m   Steg: ~a m   Tolerans: ~a%\n&amp;quot; &lt;br /&gt;
          min-L max-L step (* tol 100))&lt;br /&gt;
  (printf &amp;quot;VF: ~a   End-effect: ~a (~a% förkortning)\n\n&amp;quot;&lt;br /&gt;
          vf end-corr (round (* (- 1 end-corr) 100)))&lt;br /&gt;
&lt;br /&gt;
  (if (null? ranges)&lt;br /&gt;
      (printf &amp;quot;Inga bra längder hittades.\n&amp;quot;)&lt;br /&gt;
      (begin&lt;br /&gt;
        (for ([r ranges])&lt;br /&gt;
          (print-range r vf end-corr))&lt;br /&gt;
        (printf &amp;quot;\nTotalt ~a bra intervall (~a punkter).\n&amp;quot; &lt;br /&gt;
                (length ranges) (length goods))))&lt;br /&gt;
&lt;br /&gt;
  (printf &amp;quot;\n&amp;quot;))&lt;br /&gt;
  &lt;br /&gt;
;; =====================================================&lt;br /&gt;
;; KÖR PROGRAMMET&lt;br /&gt;
;; =====================================================&lt;br /&gt;
&lt;br /&gt;
(print-suggestions &lt;br /&gt;
  #:min-L 7        ;; Kortaste antenntråden att kika på&lt;br /&gt;
  #:max-L 50       ;; Längsta antenntråden att kika på&lt;br /&gt;
  #:step 0.01      ;; Steglängd i meter&lt;br /&gt;
  #:tol 0.03       ;; Hur nära resonans tillåter vi (3% = 0.03)&lt;br /&gt;
  #:vf 0.96        ;; Hastighetsfaktorn för plastad koppartråd&lt;br /&gt;
  #:end-corr 0.97  ;; Ändkorrigerinsfaktor&lt;br /&gt;
  )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/EFRW&amp;diff=1901</id>
		<title>Racket/EFRW</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/EFRW&amp;diff=1901"/>
		<updated>2026-06-21T21:22:09Z</updated>

		<summary type="html">&lt;p&gt;Anders: Skapade sidan med &amp;#039;&amp;lt;pre&amp;gt; #lang racket  ;; ===================================================== ;; Ändmatad antenn – längdintervall + vilka band som träffas ;; =====================================================  (define c 299.792458)   ; m×MHz  ;; Svenska band, kommentera bort de band du inte är intresserad av att köra! (define bands   &amp;#039;((1.810  2.000   &amp;quot;160m&amp;quot;)     (3.500  3.800   &amp;quot;80m&amp;quot;)     (5.3515 5.3666  &amp;quot;60m&amp;quot;)     (7.000  7.200   &amp;quot;40m&amp;quot;) ;    (10.100 10.150  &amp;quot;30m&amp;quot;)     (14.00...&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;; =====================================================&lt;br /&gt;
;; Ändmatad antenn – längdintervall + vilka band som träffas&lt;br /&gt;
;; =====================================================&lt;br /&gt;
&lt;br /&gt;
(define c 299.792458)   ; m×MHz&lt;br /&gt;
&lt;br /&gt;
;; Svenska band, kommentera bort de band du inte är intresserad av att köra!&lt;br /&gt;
(define bands&lt;br /&gt;
  &#039;((1.810  2.000   &amp;quot;160m&amp;quot;)&lt;br /&gt;
    (3.500  3.800   &amp;quot;80m&amp;quot;)&lt;br /&gt;
    (5.3515 5.3666  &amp;quot;60m&amp;quot;)&lt;br /&gt;
    (7.000  7.200   &amp;quot;40m&amp;quot;)&lt;br /&gt;
;    (10.100 10.150  &amp;quot;30m&amp;quot;)&lt;br /&gt;
    (14.000 14.350  &amp;quot;20m&amp;quot;)&lt;br /&gt;
    (18.068 18.168  &amp;quot;17m&amp;quot;)&lt;br /&gt;
    (21.000 21.450  &amp;quot;15m&amp;quot;)&lt;br /&gt;
    (24.890 24.990  &amp;quot;12m&amp;quot;)&lt;br /&gt;
    (28.000 29.700  &amp;quot;10m&amp;quot;)&lt;br /&gt;
    (50.000 52.000  &amp;quot;6m&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
;; Returnerar lista med band som L är dåliga för en given längd&lt;br /&gt;
(define (bad-bands L tol)&lt;br /&gt;
  (filter-map&lt;br /&gt;
   (λ (band)&lt;br /&gt;
     (define fmin (first band))&lt;br /&gt;
     (define fmax (second band))&lt;br /&gt;
     (define name (third band))&lt;br /&gt;
     (define fmin-forb (* fmin (- 1 tol)))&lt;br /&gt;
     (define fmax-forb (* fmax (+ 1 tol)))&lt;br /&gt;
     (for/or ([n (in-range 1 400)])&lt;br /&gt;
       (define f-res (/ (* n c) (* 2.0 L)))&lt;br /&gt;
       (and (&amp;gt;= f-res fmin-forb) (&amp;lt;= f-res fmax-forb)&lt;br /&gt;
            name)))&lt;br /&gt;
   bands))&lt;br /&gt;
&lt;br /&gt;
(define (bad-length? L tol)&lt;br /&gt;
  (not (null? (bad-bands L tol))))&lt;br /&gt;
&lt;br /&gt;
(define (good-lengths min-L max-L step tol)&lt;br /&gt;
  (for/list ([L (in-range min-L (+ max-L 0.0001) step)]&lt;br /&gt;
             #:when (not (bad-length? L tol)))&lt;br /&gt;
    L))&lt;br /&gt;
&lt;br /&gt;
(define (group-into-ranges goods step)&lt;br /&gt;
  (if (null? goods)&lt;br /&gt;
      &#039;()&lt;br /&gt;
      (let loop ([lst (sort goods &amp;lt;)]&lt;br /&gt;
                 [ranges &#039;()]&lt;br /&gt;
                 [start (first goods)]&lt;br /&gt;
                 [prev (first goods)])&lt;br /&gt;
        (cond&lt;br /&gt;
          [(null? lst) (reverse (cons (list start prev) ranges))]&lt;br /&gt;
          [else&lt;br /&gt;
           (define current (first lst))&lt;br /&gt;
           (if (&amp;lt;= (- current prev) (+ step 0.0001))&lt;br /&gt;
               (loop (rest lst) ranges start current)&lt;br /&gt;
               (loop (rest lst)&lt;br /&gt;
                     (cons (list start prev) ranges)&lt;br /&gt;
                     current&lt;br /&gt;
                     current))]))))&lt;br /&gt;
&lt;br /&gt;
(define (print-range r vf end-corr)&lt;br /&gt;
  (define elec-start (first r))&lt;br /&gt;
  (define elec-end   (second r))&lt;br /&gt;
  (define phys-start (* elec-start vf end-corr))&lt;br /&gt;
  (define phys-end   (* elec-end   vf end-corr))&lt;br /&gt;
  (define s1 (~r elec-start #:precision 1))&lt;br /&gt;
  (define s2 (~r elec-end   #:precision 1))&lt;br /&gt;
  (define p1 (~r phys-start #:precision 1))&lt;br /&gt;
  (define p2 (~r phys-end   #:precision 1))&lt;br /&gt;
  (if (equal? s1 s2)&lt;br /&gt;
      (printf &amp;quot;~a m (elektrisk)  →  ~a m (fysisk)\n&amp;quot; s1 p1)&lt;br /&gt;
      (printf &amp;quot;~a–~a m (elektrisk)  →  ~a–~a m (fysisk)\n&amp;quot; s1 s2 p1 p2)))&lt;br /&gt;
&lt;br /&gt;
;; ====================== HUVUDFUNKTION ======================&lt;br /&gt;
(define (print-suggestions&lt;br /&gt;
         #:min-L     [min-L     10.0]&lt;br /&gt;
         #:max-L     [max-L     70.0]&lt;br /&gt;
         #:step      [step      0.01]&lt;br /&gt;
         #:tol       [tol       0.03]&lt;br /&gt;
         #:vf        [vf        0.96]&lt;br /&gt;
         #:end-corr  [end-corr  0.97])&lt;br /&gt;
  &lt;br /&gt;
  (define goods (good-lengths min-L max-L step tol))&lt;br /&gt;
  (define ranges (group-into-ranges goods step))&lt;br /&gt;
&lt;br /&gt;
  (printf &amp;quot;=== Bra längdintervall för ändmatad antenn ===\n&amp;quot;)&lt;br /&gt;
  (printf &amp;quot;Intervall: ~a–~a m   Steg: ~a m   Tolerans: ~a%\n&amp;quot; &lt;br /&gt;
          min-L max-L step (* tol 100))&lt;br /&gt;
  (printf &amp;quot;VF: ~a   End-effect: ~a (~a% förkortning)\n\n&amp;quot;&lt;br /&gt;
          vf end-corr (round (* (- 1 end-corr) 100)))&lt;br /&gt;
&lt;br /&gt;
  (if (null? ranges)&lt;br /&gt;
      (printf &amp;quot;Inga bra längder hittades.\n&amp;quot;)&lt;br /&gt;
      (begin&lt;br /&gt;
        (for ([r ranges])&lt;br /&gt;
          (print-range r vf end-corr))&lt;br /&gt;
        (printf &amp;quot;\nTotalt ~a bra intervall (~a punkter).\n&amp;quot; &lt;br /&gt;
                (length ranges) (length goods))))&lt;br /&gt;
&lt;br /&gt;
  (printf &amp;quot;\n&amp;quot;))&lt;br /&gt;
  &lt;br /&gt;
;; =====================================================&lt;br /&gt;
;; KÖR PROGRAMMET&lt;br /&gt;
;; =====================================================&lt;br /&gt;
&lt;br /&gt;
(print-suggestions &lt;br /&gt;
  #:min-L 7        ;; Kortaste antenntråden att kika på&lt;br /&gt;
  #:max-L 50       ;; Längsta antenntråden att kika på&lt;br /&gt;
  #:step 0.01      ;; Steglängd i meter&lt;br /&gt;
  #:tol 0.03       ;; Hur nära resonans tillåter vi (3% = 0.03)&lt;br /&gt;
  #:vf 0.96        ;; Hastighetsfaktorn för plastad koppartråd&lt;br /&gt;
  #:end-corr 0.97  ;; Ändkorrigerinsfaktor&lt;br /&gt;
  )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket&amp;diff=1900</id>
		<title>Racket</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket&amp;diff=1900"/>
		<updated>2026-06-21T21:21:54Z</updated>

		<summary type="html">&lt;p&gt;Anders: /* Kodexempel (Sommarhack 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Racket är ett programmeringsspråk i Lispfamiljen och underfamilj till Scheme. Det är kompatibelt med Scheme R5RS och andra varianter men har också ett eget väl utbyggt bibliotek av funktioner och rutiner och kanske en av de enklaste språken att komma igång och skapa GUI-applikationer i.&lt;br /&gt;
&lt;br /&gt;
= Installation Ubuntu = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo add-apt-repository ppa:plt/racket -y&lt;br /&gt;
sudo apt update &lt;br /&gt;
sudo apt install racket&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kör igång det inbyggda IDE:t med &amp;quot;drracket&amp;quot; antingen från applikationsmenyn eller terminalen.&lt;br /&gt;
&lt;br /&gt;
= Kodexempel (Sommarhack 2026) =&lt;br /&gt;
&lt;br /&gt;
# [[Racket/Erastothenes]]&lt;br /&gt;
# [[Racket/Hexdump]]&lt;br /&gt;
# [[Racket/base24]]&lt;br /&gt;
# [[Racket/Personreg]]&lt;br /&gt;
# [[Racket/Fisher-Yates Shuffle]]&lt;br /&gt;
# [[Racket/RLE]]&lt;br /&gt;
# [[Racket/Lille-GUI]]&lt;br /&gt;
# [[Racket/Multitråd]]&lt;br /&gt;
# [[Racket/6502-emu]]&lt;br /&gt;
# [[Racket/Genetic]]&lt;br /&gt;
# [[Racket/Markov]]&lt;br /&gt;
# [[Racket/Neural]]&lt;br /&gt;
# [[Racket/Posits]]&lt;br /&gt;
# [[Racket/3D-boll]] -- från Olme&lt;br /&gt;
&lt;br /&gt;
= Andra kodexempel =&lt;br /&gt;
&lt;br /&gt;
# [[Racket/EFRW]]&lt;br /&gt;
&lt;br /&gt;
[[category:Racket]]&lt;br /&gt;
[[category:Programmering]]&lt;br /&gt;
[[category:Algoritmer]]&lt;br /&gt;
[[category:Förgreningssida]]&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Fisher-Yates_Shuffle&amp;diff=1899</id>
		<title>Racket/Fisher-Yates Shuffle</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Fisher-Yates_Shuffle&amp;diff=1899"/>
		<updated>2026-05-18T14:11:41Z</updated>

		<summary type="html">&lt;p&gt;Anders: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#lang racket&lt;br /&gt;
&lt;br /&gt;
;; Fisher-Yates shuffle för en lista (modern version, O(n))&lt;br /&gt;
(define (fisher-yates lst)&lt;br /&gt;
  (let ([v (list-&amp;gt;vector lst)])          ; Konvertera till vektor för effektiv mutation&lt;br /&gt;
    (for ([i (in-range (sub1 (vector-length v)) -1 -1)])  ; från n-1 ner till 1&lt;br /&gt;
      (let ([j (random (add1 i))])       ; slumpmässigt index 0 till i (inklusive)&lt;br /&gt;
        ;; Byt plats mellan i och j&lt;br /&gt;
        (let ([temp (vector-ref v i)])&lt;br /&gt;
          (vector-set! v i (vector-ref v j))&lt;br /&gt;
          (vector-set! v j temp))))&lt;br /&gt;
    (vector-&amp;gt;list v)))                   ; Returnera som lista igen&lt;br /&gt;
&lt;br /&gt;
;; Exempel:&lt;br /&gt;
(fisher-yates &#039;(1 2 3 4 5 6 7 8 9 10))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket/Fisher-Yates_Shuffle&amp;diff=1898</id>
		<title>Racket/Fisher-Yates Shuffle</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket/Fisher-Yates_Shuffle&amp;diff=1898"/>
		<updated>2026-05-18T14:11:24Z</updated>

		<summary type="html">&lt;p&gt;Anders: Skapade sidan med &amp;#039;#lang racket  ;; Fisher-Yates shuffle för en lista (modern version, O(n)) (define (fisher-yates lst)   (let ([v (list-&amp;gt;vector lst)])          ; Konvertera till vektor för effektiv mutation     (for ([i (in-range (sub1 (vector-length v)) -1 -1)])  ; från n-1 ner till 1       (let ([j (random (add1 i))])       ; slumpmässigt index 0 till i (inklusive)         ;; Byt plats mellan i och j         (let ([temp (vector-ref v i)])           (vector-set! v i (vector-ref v j))...&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#lang racket&lt;br /&gt;
&lt;br /&gt;
;; Fisher-Yates shuffle för en lista (modern version, O(n))&lt;br /&gt;
(define (fisher-yates lst)&lt;br /&gt;
  (let ([v (list-&amp;gt;vector lst)])          ; Konvertera till vektor för effektiv mutation&lt;br /&gt;
    (for ([i (in-range (sub1 (vector-length v)) -1 -1)])  ; från n-1 ner till 1&lt;br /&gt;
      (let ([j (random (add1 i))])       ; slumpmässigt index 0 till i (inklusive)&lt;br /&gt;
        ;; Byt plats mellan i och j&lt;br /&gt;
        (let ([temp (vector-ref v i)])&lt;br /&gt;
          (vector-set! v i (vector-ref v j))&lt;br /&gt;
          (vector-set! v j temp))))&lt;br /&gt;
    (vector-&amp;gt;list v)))                   ; Returnera som lista igen&lt;br /&gt;
&lt;br /&gt;
;; Exempel:&lt;br /&gt;
(fisher-yates &#039;(1 2 3 4 5 6 7 8 9 10))&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
	<entry>
		<id>http://wiki.sikvall.se/index.php?title=Racket&amp;diff=1897</id>
		<title>Racket</title>
		<link rel="alternate" type="text/html" href="http://wiki.sikvall.se/index.php?title=Racket&amp;diff=1897"/>
		<updated>2026-05-18T14:11:17Z</updated>

		<summary type="html">&lt;p&gt;Anders: /* Kodexempel (Sommarhack 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Racket är ett programmeringsspråk i Lispfamiljen och underfamilj till Scheme. Det är kompatibelt med Scheme R5RS och andra varianter men har också ett eget väl utbyggt bibliotek av funktioner och rutiner och kanske en av de enklaste språken att komma igång och skapa GUI-applikationer i.&lt;br /&gt;
&lt;br /&gt;
= Installation Ubuntu = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo add-apt-repository ppa:plt/racket -y&lt;br /&gt;
sudo apt update &lt;br /&gt;
sudo apt install racket&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kör igång det inbyggda IDE:t med &amp;quot;drracket&amp;quot; antingen från applikationsmenyn eller terminalen.&lt;br /&gt;
&lt;br /&gt;
= Kodexempel (Sommarhack 2026) =&lt;br /&gt;
&lt;br /&gt;
# [[Racket/Erastothenes]]&lt;br /&gt;
# [[Racket/Hexdump]]&lt;br /&gt;
# [[Racket/base24]]&lt;br /&gt;
# [[Racket/Personreg]]&lt;br /&gt;
# [[Racket/Fisher-Yates Shuffle]]&lt;br /&gt;
# [[Racket/RLE]]&lt;br /&gt;
# [[Racket/Lille-GUI]]&lt;br /&gt;
# [[Racket/Multitråd]]&lt;br /&gt;
# [[Racket/6502-emu]]&lt;br /&gt;
# [[Racket/Genetic]]&lt;br /&gt;
# [[Racket/Markov]]&lt;br /&gt;
# [[Racket/Neural]]&lt;br /&gt;
# [[Racket/Posits]]&lt;br /&gt;
# [[Racket/3D-boll]] -- från Olme&lt;br /&gt;
&lt;br /&gt;
[[category:Racket]]&lt;br /&gt;
[[category:Programmering]]&lt;br /&gt;
[[category:Algoritmer]]&lt;br /&gt;
[[category:Förgreningssida]]&lt;/div&gt;</summary>
		<author><name>Anders</name></author>
	</entry>
</feed>