summaryrefslogtreecommitdiff
path: root/controller
diff options
context:
space:
mode:
authorjaseg <git-bigdata-wsl-arch@jaseg.de>2020-05-05 17:50:09 +0200
committerjaseg <git-bigdata-wsl-arch@jaseg.de>2020-05-05 17:50:09 +0200
commitdb50711ba4a1f41f4082981bae58f213d48d96a1 (patch)
tree6480922fdc46a51bfb2e22e57d02cc0d3aef9d06 /controller
parent9918eb505321183e20357221a4dcf2aa9c1e057c (diff)
downloadmaster-thesis-db50711ba4a1f41f4082981bae58f213d48d96a1.tar.gz
master-thesis-db50711ba4a1f41f4082981bae58f213d48d96a1.tar.bz2
master-thesis-db50711ba4a1f41f4082981bae58f213d48d96a1.zip
fw: Tie together all parts for an end-to-end demo
Diffstat (limited to 'controller')
-rw-r--r--controller/fw/Makefile11
-rw-r--r--controller/fw/src/crypto.c2
-rw-r--r--controller/fw/src/crypto.h3
-rw-r--r--controller/fw/src/main.c51
-rw-r--r--controller/fw/src/protocol.c31
-rw-r--r--controller/fw/src/rscode-config.h3
-rw-r--r--controller/fw/src/rslib.c5
-rw-r--r--controller/fw/src/rslib.h5
-rw-r--r--controller/fw/tools/dsss_demod_test_runner.py17
-rw-r--r--controller/fw/tools/dsss_demod_test_waveform_gen.py21
-rwxr-xr-xcontroller/fw/tools/hum_generator.py114
-rw-r--r--controller/fw/tools/presig_gen.py2
-rw-r--r--controller/fw/tools/reed_solomon.py14
13 files changed, 234 insertions, 45 deletions
diff --git a/controller/fw/Makefile b/controller/fw/Makefile
index b6fa43f..c0f01d5 100644
--- a/controller/fw/Makefile
+++ b/controller/fw/Makefile
@@ -32,7 +32,8 @@ DSSS_WAVELET_LUT_SIZE ?= 69
DSSS_FILTER_FC ?= 3e-3
DSSS_FILTER_ORDER ?= 12
-TRANSMISSION_SYMBOLS ?= 32
+# Transmission symbols: 20 for 20*6=120 bit key + 10 for reed-solomon ECC
+TRANSMISSION_SYMBOLS ?= 30
PRESIG_STORE_SIZE ?= 3
# will be generated if necessary
@@ -47,6 +48,8 @@ C_SOURCES += src/mspdebug_wrapper.c
C_SOURCES += src/spi_flash.c
C_SOURCES += src/freq_meas.c
C_SOURCES += src/dsss_demod.c
+C_SOURCES += src/rslib.c
+C_SOURCES += src/crypto.c
C_SOURCES += src/adc.c
C_SOURCES += src/protocol.c
C_SOURCES += src/serial.c
@@ -77,6 +80,8 @@ MUSL_SOURCES += math/fabsf.c
MUSL_SOURCES += stdlib/abs.c
MUSL_SOURCES += string/memset.c
MUSL_SOURCES += string/memcpy.c
+MUSL_SOURCES += string/memcmp.c
+MUSL_SOURCES += string/strlen.c
MUSL_SOURCES += math/__math_oflowf.c
MUSL_SOURCES += math/__math_uflowf.c
MUSL_SOURCES += math/__math_xflowf.c
@@ -139,6 +144,8 @@ CFLAGS += -I$(abspath musl_include_shims)
CFLAGS += -Itinyprintf
COMMON_CFLAGS += -I$(BUILDDIR) -Isrc -Itinyaes
CFLAGS += -I$(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32F4xx/Include
+COMMON_CFLAGS += -I$(LIBSODIUM_DIR_ABS)/src/libsodium/include -I$(BUILDDIR)/libsodium/src/libsodium/include -I$(LIBSODIUM_DIR_ABS)/src/libsodium/include/sodium
+COMMON_CFLAGS += -I$(RSLIB_DIR)/src
COMMON_CFLAGS += -O0 -std=gnu11 -g -DSTM32F407xx -DSTM32F4 -DDEBUG=$(DEBUG)
CFLAGS += $(ARCH_FLAGS) $(SYSTEM_FLAGS)
@@ -187,11 +194,11 @@ ALL_OBJS := $(OBJS)
ALL_OBJS += $(BUILDDIR)/src/startup_stm32f407xx.o
ALL_OBJS += $(BUILDDIR)/src/system_stm32f4xx.o
ALL_OBJS += $(BUILDDIR)/libsodium/src/libsodium/.libs/libsodium.a
-ALL_OBJS += $(BUILDDIR)/tinyaes/aes.o
ALL_OBJS += $(BUILDDIR)/levmarq/levmarq.o
ALL_OBJS += $(BUILDDIR)/generated/gold_code_$(DSSS_GOLD_CODE_NBITS).o
ALL_OBJS += $(BUILDDIR)/generated/fmeas_fft_window.o
ALL_OBJS += $(BUILDDIR)/generated/dsss_cwt_wavelet.o
+ALL_OBJS += $(BUILDDIR)/generated/crypto_presig_data.o
########################################################################################################################
# Rules
diff --git a/controller/fw/src/crypto.c b/controller/fw/src/crypto.c
index db35745..f4f79a4 100644
--- a/controller/fw/src/crypto.c
+++ b/controller/fw/src/crypto.c
@@ -26,7 +26,7 @@ void debug_hexdump(const char *name, const uint8_t *buf, size_t len) {
DEBUG_PRINTN("\n");
}
-/* Returns 1 for correct trigger */
+/* Returns trigger sig height for correct trigger */
int verify_trigger_dom(const uint8_t inkey[PRESIG_MSG_LEN],
const char *domain_string, const uint8_t refkey[PRESIG_MSG_LEN]) {
uint8_t key[crypto_auth_hmacsha512_KEYBYTES];
diff --git a/controller/fw/src/crypto.h b/controller/fw/src/crypto.h
index 05a3c0d..18a9816 100644
--- a/controller/fw/src/crypto.h
+++ b/controller/fw/src/crypto.h
@@ -3,7 +3,8 @@
#include <stdint.h>
-#define PRESIG_MSG_LEN 16
+/* Presig message length: 15 byte = 120 bit ^= 20 * 6-bit symbols of 5-bit bipolar DSSS */
+#define PRESIG_MSG_LEN 15
#define OOB_TRIGGER_LEN PRESIG_MSG_LEN
enum trigger_domain {
diff --git a/controller/fw/src/main.c b/controller/fw/src/main.c
index 6547c38..e2f7353 100644
--- a/controller/fw/src/main.c
+++ b/controller/fw/src/main.c
@@ -15,6 +15,7 @@
#include "dsss_demod.h"
#include "con_usart.h"
#include "mspdebug_wrapper.h"
+#include "crypto.h"
static struct spi_flash_if spif;
@@ -28,6 +29,7 @@ unsigned int apb2_timer_speed = 0;
struct leds leds;
ssize_t jt_spi_flash_read_block(void *usr, int addr, size_t len, uint8_t *out);
+static void update_image_flash_counter(void);
void __libc_init_array(void) { /* we don't need this. */ }
void __assert_func (unused_a const char *file, unused_a int line, unused_a const char *function, unused_a const char *expr) {
@@ -208,7 +210,7 @@ ssize_t jt_spi_flash_read_block(void *usr, int addr, size_t len, uint8_t *out) {
return len;
}
-void update_image_flash_counter(void) {
+void update_image_flash_counter() {
static int flash_counter = 0;
flash_counter ++;
fw_dump[row2_offx + 0] = flash_counter/10000 + '0';
@@ -222,6 +224,24 @@ void update_image_flash_counter(void) {
fw_dump[row2_offx + 4] = flash_counter + '0';
}
+/* Callback from crypto.c:oob_message_received */
+void oob_trigger_activated(enum trigger_domain domain, int serial) {
+ con_printf("oob_trigger_activated(%d, %d)\r\n", domain, serial);
+ con_printf("Attempting to flash meter...\r\n");
+ update_image_flash_counter();
+
+ int flash_tries = 0;
+ while (flash_tries++ < 25) {
+ mspd_jtag_init();
+ if (!mspd_jtag_flash_and_reset(jtag_img.devmem_img_start, jtag_img.img_len, jt_spi_flash_read_block, &jtag_img))
+ break;
+ for (int j=0; j<168*1000*5; j++)
+ asm volatile ("nop");
+ }
+ if (flash_tries == 25)
+ con_printf("Giving up.\r\n");
+}
+
static unsigned int measurement_errors = 0;
static struct dsss_demod_state demod_state;
static uint32_t freq_sample_ts = 0;
@@ -264,17 +284,24 @@ int main(void)
dsss_demod_init(&demod_state);
con_printf("Booted.\r\n");
- con_printf("Attempting to flash meter...\r\n");
- int flash_tries = 0;
- while (flash_tries++ < 25) {
- mspd_jtag_init();
- if (!mspd_jtag_flash_and_reset(jtag_img.devmem_img_start, jtag_img.img_len, jt_spi_flash_read_block, &jtag_img))
- break;
- for (int j=0; j<168*1000*5; j++)
- asm volatile ("nop");
- }
- if (flash_tries == 25)
- con_printf("Giving up.\r\n");
+
+
+ /* FIXME DEBUG */
+#if 0
+ uint8_t test_data[TRANSMISSION_SYMBOLS] = {
+ 0
+ };
+ con_printf("Test 0\r\n");
+ handle_dsss_received(test_data);
+
+ uint8_t test_data2[TRANSMISSION_SYMBOLS] = {
+ 0x24, 0x0f, 0x3b, 0x10, 0x27, 0x0e, 0x22, 0x30, 0x01, 0x2c, 0x1c, 0x0b, 0x35, 0x0a, 0x12, 0x27, 0x11, 0x20,
+ 0x0c, 0x10, 0xc0, 0x08, 0xa4, 0x72, 0xa9, 0x9b, 0x7b, 0x27, 0xee, 0xcd
+ };
+ con_printf("Test 1\r\n");
+ handle_dsss_received(test_data2);
+#endif
+ /* END DEBUG */
while (23) {
if (adc_fft_buf_ready_idx != -1) {
diff --git a/controller/fw/src/protocol.c b/controller/fw/src/protocol.c
index 4740917..6b7d8b7 100644
--- a/controller/fw/src/protocol.c
+++ b/controller/fw/src/protocol.c
@@ -1,9 +1,14 @@
+#include <assert.h>
+
#include "sr_global.h"
#include "dsss_demod.h"
#include "con_usart.h"
+#include "rslib.h"
+#include "crypto.h"
void handle_dsss_received(uint8_t data[static TRANSMISSION_SYMBOLS]) {
+ /* Console status output */
con_printf("DSSS data received: ");
for (int i=0; i<TRANSMISSION_SYMBOLS; i++) {
int x = (data[i]>>1) * (data[i]&1 ? 1 : -1);
@@ -11,5 +16,29 @@ void handle_dsss_received(uint8_t data[static TRANSMISSION_SYMBOLS]) {
}
con_printf("\r\n");
- update_image_flash_counter();
+ /* Run reed-solomon error correction */
+ const int sym_bits = DSSS_GOLD_CODE_NBITS + 1; /* +1 for amplitude sign bit */
+ /* TODO identify erasures in DSSS demod layer */
+ (void) rslib_decode(sym_bits, TRANSMISSION_SYMBOLS, (char *)data);
+ /* TODO error detection & handling */
+
+ /* Re-bit-pack data buffer to be bit-continuous:
+ * [ . . a b c d e f ] [ . . g h i j k l ] [ . . m n o p q r ] ...
+ * ==> [ a b c d e f g h ] [ i j k l m n o p ] [ q r ... ] ...
+ */
+ static_assert((TRANSMISSION_SYMBOLS - NPAR) * (DSSS_GOLD_CODE_NBITS + 1) == OOB_TRIGGER_LEN * 8);
+ for (uint8_t i=0, j=0; i < TRANSMISSION_SYMBOLS - NPAR; i++, j += sym_bits) {
+ uint32_t sym = data[i]; /* [ ... | . . X X X X X X ] for 5-bit dsss */
+ data[i] = 0; /* clear for output */
+
+ sym <<= 8-sym_bits; /* left-align: [ ... | X X X X X X . . ] */
+ sym <<= 8; /* shift to second byte: [ ... | X X X X X X . . | . . . . . . . . ]*/
+ sym >>= (j%8); /* shift to bit write offset: [ ... | . . . . X X X X | X X . . . . . . ] for offset 4 */
+ data[j/8] |= sym >> 8; /* write upper byte */
+ data[j/8 + 1] |= sym & 0xff; /* write lower byte */
+ }
+
+ /* hand off to crypto.c */
+ oob_message_received(data);
}
+
diff --git a/controller/fw/src/rscode-config.h b/controller/fw/src/rscode-config.h
index 922aca9..ea5183b 100644
--- a/controller/fw/src/rscode-config.h
+++ b/controller/fw/src/rscode-config.h
@@ -3,7 +3,6 @@
#ifndef __RSCODE_CONFIG_H__
#define __RSCODE_CONFIG_H__
-#define NPAR 4
-#define NBITS 6
+#define NPAR 10
#endif /* __RSCODE_CONFIG_H__ */
diff --git a/controller/fw/src/rslib.c b/controller/fw/src/rslib.c
index ce54a6f..aa0db2c 100644
--- a/controller/fw/src/rslib.c
+++ b/controller/fw/src/rslib.c
@@ -6,20 +6,19 @@
#include "rslib.h"
+static struct rscode_driver driver;
+
void rslib_encode(int nbits, size_t msglen, char msg[static msglen], char out[msglen + NPAR]) {
- struct rscode_driver driver;
rscode_init(&driver, nbits);
rscode_encode(&driver, (unsigned char *)msg, msglen, (unsigned char *)out);
}
int rslib_decode(int nbits, size_t msglen, char msg_inout[static msglen]) {
- struct rscode_driver driver;
rscode_init(&driver, nbits);
return rscode_decode(&driver, (unsigned char *)msg_inout, msglen);
}
int rslib_gexp(int z, int nbits) {
- struct rscode_driver driver;
rscode_init(&driver, nbits);
return gexp(&driver, z);
}
diff --git a/controller/fw/src/rslib.h b/controller/fw/src/rslib.h
index 3ef6d19..bba8bb0 100644
--- a/controller/fw/src/rslib.h
+++ b/controller/fw/src/rslib.h
@@ -1,9 +1,12 @@
#ifndef __RSLIB_H__
#define __RSLIB_H__
+/* parity length configuration */
+#include "rscode-config.h"
+
void rslib_encode(int nbits, size_t msglen, char msg[static msglen], char out[msglen + NPAR]);
int rslib_decode(int nbits, size_t msglen, char msg_inout[static msglen]);
int rslib_gexp(int z, int nbits);
-size_t rslib_npar();
+size_t rslib_npar(void);
#endif /* __RSLIB_H__ */
diff --git a/controller/fw/tools/dsss_demod_test_runner.py b/controller/fw/tools/dsss_demod_test_runner.py
index 27a0c8e..d3c3cfc 100644
--- a/controller/fw/tools/dsss_demod_test_runner.py
+++ b/controller/fw/tools/dsss_demod_test_runner.py
@@ -12,15 +12,13 @@ import multiprocessing
import sqlite3
import time
from urllib.parse import urlparse
-import functools
import tempfile
import itertools
import numpy as np
np.set_printoptions(linewidth=240)
-from dsss_demod_test_waveform_gen import load_noise_meas_params, load_noise_synth_params,\
- mains_noise_measured, mains_noise_synthetic, modulate as dsss_modulate
+from dsss_demod_test_waveform_gen import load_noise_gen, modulate as dsss_modulate
def build_test_binary(nbits, thf, decimation, symbols, cachedir):
@@ -46,19 +44,6 @@ def build_test_binary(nbits, thf, decimation, symbols, cachedir):
return build_id
-@functools.lru_cache()
-def load_noise_gen(url):
- schema, refpath = url.split('://')
- if not path.isabs(refpath):
- refpath = path.abspath(path.join(path.dirname(__file__), refpath))
-
- if schema == 'meas':
- return mains_noise_measured, load_noise_meas_params(refpath)
- elif schema == 'synth':
- return mains_noise_synthetic, load_noise_synth_params(refpath)
- else:
- raise ValueError('Invalid schema', schema)
-
def sequence_matcher(test_data, decoded, max_shift=3):
match_result = []
for shift in range(-max_shift, max_shift):
diff --git a/controller/fw/tools/dsss_demod_test_waveform_gen.py b/controller/fw/tools/dsss_demod_test_waveform_gen.py
index 1749bd7..414c553 100644
--- a/controller/fw/tools/dsss_demod_test_waveform_gen.py
+++ b/controller/fw/tools/dsss_demod_test_waveform_gen.py
@@ -1,4 +1,6 @@
+from os import path
+import json
import functools
import numpy as np
@@ -53,9 +55,9 @@ def mains_noise_measured(seed, n, meas_data):
def load_noise_synth_params(specfile):
with open(specfile) as f:
d = json.load(f)
- return (np.linspace(*d['x_spec']), # spl_x
- d['x_spec'][2], # spl_N
- (d['t'], d['c'], d['k'])) # psd_spl
+ return {'spl_x': np.linspace(*d['x_spec']),
+ 'spl_N': d['x_spec'][2],
+ 'psd_spl': (d['t'], d['c'], d['k']) }
def mains_noise_synthetic(seed, n, psd_spl, spl_N, spl_x):
st = np.random.RandomState(seed)
@@ -69,3 +71,16 @@ def mains_noise_synthetic(seed, n, psd_spl, spl_N, spl_x):
renoise = scipy.fftpack.ifft(spec)
return renoise[10000:][:n] + 50.00
+@functools.lru_cache()
+def load_noise_gen(url):
+ schema, refpath = url.split('://')
+ if not path.isabs(refpath):
+ refpath = path.abspath(path.join(path.dirname(__file__), refpath))
+
+ if schema == 'meas':
+ return mains_noise_measured, load_noise_meas_params(refpath)
+ elif schema == 'synth':
+ return mains_noise_synthetic, load_noise_synth_params(refpath)
+ else:
+ raise ValueError('Invalid schema', schema)
+
diff --git a/controller/fw/tools/hum_generator.py b/controller/fw/tools/hum_generator.py
new file mode 100755
index 0000000..a139491
--- /dev/null
+++ b/controller/fw/tools/hum_generator.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+import binascii
+import struct
+
+import numpy as np
+import pydub
+
+from dsss_demod_test_waveform_gen import load_noise_gen, modulate as dsss_modulate
+
+np.set_printoptions(linewidth=240)
+
+def generate_noisy_signal(
+ test_data=32,
+ test_nbits=5,
+ test_decimation=10,
+ test_signal_amplitude=20e-3,
+ noise_level=10e-3,
+ noise_spec='synth://grid_freq_psd_spl_108pt.json',
+ f_nom=50.0,
+ seed=0):
+
+ #test_data = np.random.RandomState(seed=0).randint(0, 2 * (2**test_nbits), test_duration)
+ #test_data = np.array([0, 1, 2, 3] * 50)
+ if isinstance(test_data, int):
+ test_data = np.array(range(test_data))
+
+
+ signal = np.repeat(dsss_modulate(test_data, test_nbits) * 2.0 - 1, test_decimation)
+
+ noise_gen, noise_params = load_noise_gen(noise_spec)
+ noise = noise_gen(seed, len(signal), **noise_params)
+ return np.absolute(noise + f_nom + signal*test_signal_amplitude)
+
+def write_raw_frequencies_bin(outfile, **kwargs):
+ with open(outfile, 'wb') as f:
+ for x in generate_noisy_signal(**kwargs):
+ f.write(struct.pack('f', x))
+
+def synthesize_sine(freqs, freqs_sampling_rate=10.0, output_sampling_rate=44100):
+ duration = len(freqs) / freqs_sampling_rate # seconds
+ afreq_out = np.interp(np.linspace(0, duration, int(duration*output_sampling_rate)), np.linspace(0, duration, len(freqs)), freqs)
+ return np.sin(np.cumsum(2*np.pi * afreq_out / output_sampling_rate))
+
+def write_flac(filename, signal, sampling_rate=44100):
+ signal -= np.min(signal)
+ signal /= np.max(signal)
+ signal -= 0.5
+ signal *= 2**16 - 1
+ le_bytes = signal.astype(np.int16).tobytes()
+ seg = pydub.AudioSegment(data=le_bytes, sample_width=2, frame_rate=sampling_rate, channels=1)
+ seg.export(filename, format='flac')
+
+def write_synthetic_hum_flac(filename, output_sampling_rate=44100, freqs_sampling_rate=10.0, **kwargs):
+ signal = generate_noisy_signal(**kwargs)
+ print(signal)
+ write_flac(filename, synthesize_sine(signal, freqs_sampling_rate, output_sampling_rate),
+ sampling_rate=output_sampling_rate)
+
+def emulate_adc_signal(adc_bits=12, adc_offset=0.4, adc_amplitude=0.25, freq_sampling_rate=10.0, output_sampling_rate=1000, **kwargs):
+ signal = synthesize_sine(generate_noisy_signal(), freq_sampling_rate, output_sampling_rate)
+ signal = signal*adc_amplitude + adc_offset
+ smin, smax = np.min(signal), np.max(signal)
+ if smin < 0.0 or smax > 1.0:
+ raise UserWarning('Amplitude or offset too large: Signal out of bounds with min/max [{smin}, {smax}] of ADC range')
+ signal *= 2**adc_bits -1
+ return signal
+
+def save_adc_signal(fn, signal, dtype=np.uint16):
+ with open(fn, 'wb') as f:
+ f.write(signal.astype(dtype).tobytes())
+
+def write_emulated_adc_signal_bin(filename, **kwargs):
+ save_adc_signal(filename, emulate_adc_signal(**kwargs))
+
+def hum_cmd(args):
+ write_synthetic_hum_flac(args.out_flac,
+ output_sampling_rate=args.audio_sampling_rate,
+ freqs_sampling_rate=args.frequency_sampling_rate,
+ test_data = np.array(list(binascii.unhexlify(args.data))),
+ test_nbits = args.symbol_bits,
+ test_decimation = args.decimation,
+ test_signal_amplitude = args.signal_level/1e3,
+ noise_level = args.noise_level/1e3,
+ noise_spec=args.noise_spec,
+ f_nom = args.nominal_frequency,
+ seed = args.random_seed)
+
+
+if __name__ == '__main__':
+ import argparse
+ parser = argparse.ArgumentParser()
+ cmd_parser = parser.add_subparsers(required=True)
+ hum_parser = cmd_parser.add_parser('hum', help='Generated artificial modulated mains hum')
+ # output parameters
+ hum_parser.add_argument('-a', '--audio-sampling-rate', type=int, default=44100)
+
+ # modulation parameters
+ hum_parser.add_argument('-f', '--frequency-sampling-rate', type=float, default=10.0*100/128)
+ hum_parser.add_argument('-b', '--symbol-bits', type=int, default=5, help='bits per symbol (excluding sign bit)')
+ hum_parser.add_argument('-n', '--noise-level', type=float, default=1.0, help='Scale synthetic noise level')
+ hum_parser.add_argument('-s', '--signal-level', type=float, default=20.0, help='Synthetic noise level in mHz')
+ hum_parser.add_argument('-d', '--decimation', type=int, default=10, help='DSSS modulation decimation in frequency measurement cycles')
+ hum_parser.add_argument('-o', '--nominal-frequency', type=float, default=50.0, help='Nominal mains frequency')
+ hum_parser.add_argument('-r', '--random-seed', type=int, default=0)
+ hum_parser.add_argument('--noise-spec', type=str, default='synth://grid_freq_psd_spl_108pt.json')
+ hum_parser.add_argument('out_flac', metavar='out.flac', help='FLAC output file')
+ hum_parser.add_argument('data', help='modulation data hex string')
+ hum_parser.set_defaults(func=hum_cmd)
+
+ args = parser.parse_args()
+ args.func(args)
+
diff --git a/controller/fw/tools/presig_gen.py b/controller/fw/tools/presig_gen.py
index 3f85522..c5dafe7 100644
--- a/controller/fw/tools/presig_gen.py
+++ b/controller/fw/tools/presig_gen.py
@@ -9,7 +9,7 @@ import binascii
import time
from datetime import datetime
-LINKING_KEY_SIZE = 16
+LINKING_KEY_SIZE = 15
PRESIG_VERSION = '000.001'
DOMAINS = ['all', 'country', 'region', 'vendor', 'series']
diff --git a/controller/fw/tools/reed_solomon.py b/controller/fw/tools/reed_solomon.py
index 9eee6be..c4ca6e4 100644
--- a/controller/fw/tools/reed_solomon.py
+++ b/controller/fw/tools/reed_solomon.py
@@ -64,6 +64,14 @@ def cmdline_func_test(args, print=lambda *args, **kwargs: None, benchmark=False)
).decode().replace('0', '.'))
assert test_data == decoded
+def cmdline_func_encode(args, **kwargs):
+ data = np.frombuffer(binascii.unhexlify(args.hex_str), dtype=np.uint8)
+ # Map 8 bit input to 6 bit symbol string
+ data = np.packbits(np.pad(np.unpackbits(data).reshape((-1, 6)), ((0,0),(2, 0))).flatten())
+ encoded = encode(data.tobytes(), nbits=args.bits)
+ print('symbol array:', ', '.join(f'0x{x:02x}' for x in encoded))
+ print('hex string:', binascii.hexlify(encoded).decode())
+
if __name__ == '__main__':
parser = argparse.ArgumentParser()
cmd_parser = parser.add_subparsers(required=True)
@@ -75,7 +83,9 @@ if __name__ == '__main__':
test_parser.add_argument('-s', '--seed', type=int, default=0, help='Random seed')
test_parser.set_defaults(func=cmdline_func_test)
enc_parser = cmd_parser.add_parser('encode', help='RS-Encode given hex string')
- enc_parser.add_argument('hex_str')
+ enc_parser.set_defaults(func=cmdline_func_encode)
+ enc_parser.add_argument('-b', '--bits', type=int, default=8, help='Symbol bit size')
+ enc_parser.add_argument('hex_str', type=str, help='Input data as hex string')
args = parser.parse_args()
- args.func(args, print=print)
+ args.func(args)