-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathReedSolomonDemo.java
173 lines (145 loc) · 6.12 KB
/
ReedSolomonDemo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*
* Reed-Solomon error-correcting code decoder demo (Java)
*
* Copyright (c) 2019 Project Nayuki
* All rights reserved. Contact Nayuki for licensing.
* https://www.nayuki.io/page/reed-solomon-error-correcting-code-decoder
*/
import java.util.Arrays;
import java.util.Random;
public final class ReedSolomonDemo {
// Runs a bunch of demos and tests, printing information to standard error.
public static void main(String[] args) {
showBinaryExample();
showPrimeExample();
testCorrectness();
}
// Shows an example of encoding a binary message, and decoding a codeword containing errors.
private static void showBinaryExample() {
// Configurable parameters
BinaryField field = new BinaryField(0x11D);
Integer generator = 0x02;
int msgLen = 8;
int eccLen = 5;
ReedSolomon<Integer> rs = new ReedSolomon<>(field, generator, Integer.class, msgLen, eccLen);
// Generate random message
Integer[] message = new Integer[msgLen];
for (int i = 0; i < message.length; i++)
message[i] = rand.nextInt(field.size);
System.err.println("Original message: " + Arrays.toString(message));
// Encode message to produce codeword
Integer[] codeword = rs.encode(message);
System.err.println("Encoded codeword: " + Arrays.toString(codeword));
// Perturb some values in the codeword
double probability = (double)(eccLen / 2) / (msgLen + eccLen);
int perturbed = 0;
for (int i = 0; i < codeword.length; i++) {
if (rand.nextDouble() < probability) {
codeword[i] = field.add(codeword[i], rand.nextInt(field.size - 1) + 1);
perturbed++;
}
}
System.err.println("Number of values perturbed: " + perturbed);
System.err.println("Perturbed codeword: " + Arrays.toString(codeword));
// Try to decode the codeword
Integer[] decoded = rs.decode(codeword);
System.err.println("Decoded message: " + (decoded != null ? Arrays.toString(decoded) : "Failure"));
System.err.println();
}
// Shows an example of encoding a prime message, and decoding a codeword containing errors.
private static void showPrimeExample() {
// Configurable parameters
PrimeField field = new PrimeField(97);
Integer generator = 5;
int msgLen = 9;
int eccLen = 4;
ReedSolomon<Integer> rs = new ReedSolomon<>(field, generator, Integer.class, msgLen, eccLen);
// Generate random message
Integer[] message = new Integer[msgLen];
for (int i = 0; i < message.length; i++)
message[i] = rand.nextInt(field.modulus);
System.err.println("Original message: " + Arrays.toString(message));
// Encode message to produce codeword
Integer[] codeword = rs.encode(message);
System.err.println("Encoded codeword: " + Arrays.toString(codeword));
// Perturb some values in the codeword
double probability = (double)(eccLen / 2) / (msgLen + eccLen);
int perturbed = 0;
for (int i = 0; i < codeword.length; i++) {
if (rand.nextDouble() < probability) {
codeword[i] = field.add(codeword[i], rand.nextInt(field.modulus - 1) + 1);
perturbed++;
}
}
System.err.println("Number of values perturbed: " + perturbed);
System.err.println("Perturbed codeword: " + Arrays.toString(codeword));
// Try to decode the codeword
Integer[] decoded = rs.decode(codeword);
System.err.println("Decoded message: " + (decoded != null ? Arrays.toString(decoded) : "Failure"));
System.err.println();
}
// Tests the Reed-Solomon encoding and decoding logic under many parameters with many repetitions.
// This prints the results of each test round, and loops infinitely unless
// stopped by an exception (which should not happen if correctly designed).
// - Whenever numErrors <= floor(eccLen / 2), the decoding will always succeed,
// otherwise the implementation is faulty.
// - Whenever numErrors > floor(eccLen / 2), failures and wrong answers are perfectly normal,
// and success is generally not expected (but still possible).
private static void testCorrectness() {
// Field parameters
BinaryField field = new BinaryField(0x11D);
Integer generator = 0x02;
// Run forever unless an exception is thrown or unexpected behavior is encountered
int testDuration = 3000; // In milliseconds
while (true) {
// Choose random Reed-Solomon parameters
int msgLen = rand.nextInt(field.size) + 1;
int eccLen = rand.nextInt(field.size) + 1;
int codewordLen = msgLen + eccLen;
if (codewordLen > field.size - 1)
continue;
int numErrors = rand.nextInt(codewordLen + 1);
ReedSolomon<Integer> rs = new ReedSolomon<>(field, generator, Integer.class, msgLen, eccLen);
// Do as many trials as possible in a fixed amount of time
long numSuccess = 0;
long numWrong = 0;
long numFailure = 0;
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < testDuration) {
// Generate random message
Integer[] message = new Integer[msgLen];
for (int i = 0; i < message.length; i++)
message[i] = rand.nextInt(field.size);
// Encode message to codeword
Integer[] codeword = rs.encode(message);
// Perturb values in the codeword
int[] indexes = new int[codewordLen];
for (int i = 0; i < indexes.length; i++)
indexes[i] = i;
for (int i = 0; i < numErrors; i++) {
// Partial Durstenfeld shuffle
int j = rand.nextInt(indexes.length - i) + i;
int temp = indexes[i];
indexes[i] = indexes[j];
indexes[j] = temp;
// Actual perturbation
codeword[indexes[i]] ^= rand.nextInt(field.size - 1) + 1;
}
// Try to decode the codeword, and evaluate result
Integer[] decoded = rs.decode(codeword);
if (Arrays.equals(decoded, message))
numSuccess++;
else if (numErrors <= eccLen / 2)
throw new AssertionError("Decoding should have succeeded");
else if (decoded != null)
numWrong++;
else
numFailure++;
}
// Print parameters and statistics for this round
System.err.printf("msgLen=%d, eccLen=%d, codewordLen=%d, numErrors=%d; numTrials=%d, numSuccess=%d, numWrong=%d, numFailure=%d%n",
msgLen, eccLen, codewordLen, numErrors, numSuccess + numWrong + numFailure, numSuccess, numWrong, numFailure);
}
}
private static final Random rand = new Random();
}