mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2024-12-13 03:34:35 +00:00
Yet another POCSAG bugfix (multi-batch messages are not cut anymore)
Added BCH ECC functions for checking, error correction and encoding
This commit is contained in:
parent
98f89a84bb
commit
24abe4b427
@ -135,14 +135,15 @@ set(CPPSRC
|
||||
debounce.cpp
|
||||
touch.cpp
|
||||
touch_adc.cpp
|
||||
encoder.cpp
|
||||
audio.cpp
|
||||
adsb.cpp
|
||||
afsk.cpp
|
||||
audio.cpp
|
||||
${COMMON}/bch_code.cpp
|
||||
bht.cpp
|
||||
ctcss.cpp
|
||||
rds.cpp
|
||||
encoder.cpp
|
||||
freqman.cpp
|
||||
rds.cpp
|
||||
${COMMON}/lcd_ili9341.cpp
|
||||
${COMMON}/ui.cpp
|
||||
${COMMON}/ui_text.cpp
|
||||
|
@ -2030,6 +2030,33 @@ __/common/ais_packet.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/ais_packet.cpp.s
|
||||
.PHONY : __/common/ais_packet.cpp.s
|
||||
|
||||
__/common/bch_code.obj: __/common/bch_code.cpp.obj
|
||||
|
||||
.PHONY : __/common/bch_code.obj
|
||||
|
||||
# target to build an object file
|
||||
__/common/bch_code.cpp.obj:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/bch_code.cpp.obj
|
||||
.PHONY : __/common/bch_code.cpp.obj
|
||||
|
||||
__/common/bch_code.i: __/common/bch_code.cpp.i
|
||||
|
||||
.PHONY : __/common/bch_code.i
|
||||
|
||||
# target to preprocess a source file
|
||||
__/common/bch_code.cpp.i:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/bch_code.cpp.i
|
||||
.PHONY : __/common/bch_code.cpp.i
|
||||
|
||||
__/common/bch_code.s: __/common/bch_code.cpp.s
|
||||
|
||||
.PHONY : __/common/bch_code.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
__/common/bch_code.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/bch_code.cpp.s
|
||||
.PHONY : __/common/bch_code.cpp.s
|
||||
|
||||
__/common/buffer.obj: __/common/buffer.cpp.obj
|
||||
|
||||
.PHONY : __/common/buffer.obj
|
||||
@ -5544,6 +5571,9 @@ help:
|
||||
@echo "... __/common/ais_packet.obj"
|
||||
@echo "... __/common/ais_packet.i"
|
||||
@echo "... __/common/ais_packet.s"
|
||||
@echo "... __/common/bch_code.obj"
|
||||
@echo "... __/common/bch_code.i"
|
||||
@echo "... __/common/bch_code.s"
|
||||
@echo "... __/common/buffer.obj"
|
||||
@echo "... __/common/buffer.i"
|
||||
@echo "... __/common/buffer.s"
|
||||
|
@ -36,7 +36,7 @@ using namespace pocsag;
|
||||
#include "utility.hpp"
|
||||
|
||||
void POCSAGLogger::on_packet(const pocsag::POCSAGPacket& packet, const uint32_t frequency) {
|
||||
std::string entry = pocsag::bitrate_str(packet.bitrate()) + " " + to_string_dec_uint(frequency) + "Hz ";
|
||||
std::string entry = to_string_dec_uint(frequency) + "Hz " + pocsag::bitrate_str(packet.bitrate()) + " RAW: ";
|
||||
|
||||
// Raw hex dump of all the codewords
|
||||
for (size_t c = 0; c < 16; c++)
|
||||
@ -47,20 +47,15 @@ void POCSAGLogger::on_packet(const pocsag::POCSAGPacket& packet, const uint32_t
|
||||
|
||||
void POCSAGLogger::on_decoded(
|
||||
const pocsag::POCSAGPacket& packet,
|
||||
const std::string info,
|
||||
const std::string text) {
|
||||
|
||||
// Decoded address and message
|
||||
log_file.write_entry(packet.timestamp(), info);
|
||||
log_file.write_entry(packet.timestamp(), text);
|
||||
}
|
||||
|
||||
namespace ui {
|
||||
|
||||
void POCSAGAppView::update_freq(rf::Frequency f) {
|
||||
char finalstr[10] = { 0 };
|
||||
|
||||
options_freq.set_selected_index(0);
|
||||
options_freq.set_selected_index(0); // "Entered"
|
||||
set_target_frequency(f);
|
||||
|
||||
portapack::persistent_memory::set_tuned_frequency(f); // Maybe not ?
|
||||
@ -68,14 +63,11 @@ void POCSAGAppView::update_freq(rf::Frequency f) {
|
||||
auto mhz = to_string_dec_int(f / 1000000, 4);
|
||||
auto hz100 = to_string_dec_int((f / 100) % 10000, 4, '0');
|
||||
|
||||
strcat(finalstr, mhz.c_str());
|
||||
strcat(finalstr, ".");
|
||||
strcat(finalstr, hz100.c_str());
|
||||
|
||||
this->button_setfreq.set_text(finalstr);
|
||||
this->button_setfreq.set_text(mhz + "." + hz100);
|
||||
}
|
||||
|
||||
POCSAGAppView::POCSAGAppView(NavigationView& nav) {
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_pocsag);
|
||||
|
||||
add_children({
|
||||
@ -91,15 +83,19 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
|
||||
&console
|
||||
});
|
||||
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
receiver_model.enable();
|
||||
|
||||
check_log.set_value(logging);
|
||||
check_log.on_select = [this](Checkbox&, bool v) {
|
||||
logging = v;
|
||||
};
|
||||
|
||||
options_bitrate.set_selected_index(1); // 1200bps
|
||||
options_bitrate.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
this->on_bitrate_changed(v);
|
||||
};
|
||||
options_bitrate.set_selected_index(1); // 1200bps
|
||||
|
||||
options_freq.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
this->on_band_changed(v);
|
||||
@ -116,19 +112,13 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
|
||||
logger = std::make_unique<POCSAGLogger>();
|
||||
if (logger) logger->append("pocsag.txt");
|
||||
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
receiver_model.enable();
|
||||
|
||||
const auto new_volume = volume_t::decibel(60 - 99) + audio::headphone::volume_range().max;
|
||||
/*const auto new_volume = volume_t::decibel(60 - 99) + audio::headphone::volume_range().max;
|
||||
receiver_model.set_headphone_volume(new_volume);
|
||||
audio::output::start();
|
||||
|
||||
baseband::set_pocsag(pocsag::BitRate::FSK1200);
|
||||
audio::output::start();*/
|
||||
}
|
||||
|
||||
POCSAGAppView::~POCSAGAppView() {
|
||||
audio::output::stop();
|
||||
//audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@ -145,8 +135,9 @@ void POCSAGAppView::set_parent_rect(const Rect new_parent_rect) {
|
||||
void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) {
|
||||
std::string alphanum_text = "";
|
||||
|
||||
// Log raw data
|
||||
if (logger && logging) logger->on_packet(message->packet, target_frequency());
|
||||
// Log raw data whatever it contains
|
||||
if (logger && logging)
|
||||
logger->on_packet(message->packet, target_frequency());
|
||||
|
||||
if (message->packet.flag() != NORMAL) {
|
||||
console.writeln(
|
||||
@ -155,45 +146,47 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) {
|
||||
);
|
||||
}
|
||||
|
||||
bool result = decode_batch(message->packet, &pocsag_state);
|
||||
bool result = pocsag_decode_batch(message->packet, &pocsag_state);
|
||||
|
||||
if (result) {
|
||||
std::string console_info;
|
||||
|
||||
console_info = to_string_time(message->packet.timestamp()) + " ";
|
||||
console_info += pocsag::bitrate_str(message->packet.bitrate());
|
||||
console_info += " ADDR:" + to_string_dec_uint(pocsag_state.address);
|
||||
console_info += " F" + to_string_dec_uint(pocsag_state.function);
|
||||
|
||||
if (pocsag_state.out_type == ADDRESS) {
|
||||
// Address only
|
||||
console_info = to_string_time(message->packet.timestamp()) + " ";
|
||||
console_info += pocsag::bitrate_str(message->packet.bitrate()) + " ";
|
||||
console_info += "ADDR:" + to_string_dec_uint(pocsag_state.address);
|
||||
console_info += " F" + to_string_dec_uint(pocsag_state.function);
|
||||
|
||||
console.writeln(console_info);
|
||||
|
||||
if (logger) logger->on_decoded(message->packet, console_info, pocsag_state.output);
|
||||
if (logger && logging)
|
||||
logger->on_decoded(message->packet, console_info);
|
||||
|
||||
last_address = pocsag_state.address;
|
||||
} else if (pocsag_state.out_type == MESSAGE) {
|
||||
if (pocsag_state.address != last_address) {
|
||||
// New message
|
||||
console_info = to_string_time(message->packet.timestamp()) + " ";
|
||||
console_info += pocsag::bitrate_str( message->packet.bitrate()) + " ";
|
||||
console_info += "ADDR:" + to_string_dec_uint(pocsag_state.address);
|
||||
console_info += " F" + to_string_dec_uint(pocsag_state.function);
|
||||
|
||||
console.writeln(console_info);
|
||||
|
||||
console.writeln("Alpha:" + pocsag_state.output);
|
||||
|
||||
if (logger) logger->on_decoded(message->packet, console_info, pocsag_state.output);
|
||||
if (logger && logging) {
|
||||
logger->on_decoded(message->packet, console_info);
|
||||
logger->on_decoded(message->packet, pocsag_state.output);
|
||||
}
|
||||
|
||||
last_address = pocsag_state.address;
|
||||
} else {
|
||||
// Message continues...
|
||||
console.writeln(pocsag_state.output);
|
||||
|
||||
if (logger && logging)
|
||||
logger->on_decoded(message->packet, pocsag_state.output);
|
||||
}
|
||||
} else if (pocsag_state.out_type == EMPTY)
|
||||
console.writeln("-");
|
||||
|
||||
console.write(pocsag_state.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,12 +27,13 @@
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_rssi.hpp"
|
||||
#include "ui_channel.hpp"
|
||||
//#include "ui_channel.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "log_file.hpp"
|
||||
|
||||
#include "bch_code.hpp"
|
||||
#include "pocsag.hpp"
|
||||
#include "pocsag_packet.hpp"
|
||||
|
||||
@ -43,7 +44,7 @@ public:
|
||||
}
|
||||
|
||||
void on_packet(const pocsag::POCSAGPacket& packet, const uint32_t frequency);
|
||||
void on_decoded(const pocsag::POCSAGPacket& packet, const std::string info, const std::string text);
|
||||
void on_decoded(const pocsag::POCSAGPacket& packet, const std::string text);
|
||||
|
||||
private:
|
||||
LogFile log_file { };
|
||||
@ -69,7 +70,12 @@ private:
|
||||
static constexpr uint32_t sampling_rate = 3072000;
|
||||
//static constexpr uint32_t baseband_bandwidth = 1750000;
|
||||
|
||||
bool logging { false };
|
||||
BCHCode BCH_code {
|
||||
{ 1, 0, 1, 0, 0, 1 },
|
||||
5, 31, 21, 2
|
||||
};
|
||||
|
||||
bool logging { true };
|
||||
uint32_t last_address = 0xFFFFFFFF;
|
||||
pocsag::POCSAGState pocsag_state { };
|
||||
|
||||
|
@ -47,7 +47,7 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
|
||||
const int32_t audio_sample = __SSAT(sample_int, 16);
|
||||
|
||||
slicer_sr <<= 1;
|
||||
slicer_sr |= (audio_sample < 0);
|
||||
slicer_sr |= (audio_sample < 0); // Do we need hysteresis ?
|
||||
|
||||
// Detect transitions to adjust clock
|
||||
if ((slicer_sr ^ (slicer_sr >> 1)) & 1) {
|
||||
@ -79,19 +79,18 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (sync_timeout < POCSAG_TIMEOUT) {
|
||||
sync_timeout++;
|
||||
|
||||
if (rx_data == POCSAG_SYNC) {
|
||||
if (rx_data == POCSAG_SYNCWORD) {
|
||||
packet.clear();
|
||||
codeword_count = 0;
|
||||
rx_bit = 0;
|
||||
msg_timeout = 0;
|
||||
rx_state = SYNC;
|
||||
} /*else if (rx_data == POCSAG_IDLE) {
|
||||
rx_state = WAITING;
|
||||
}*/
|
||||
}
|
||||
|
||||
} else {
|
||||
rx_state = WAITING; // Timed out: abort
|
||||
push_packet(pocsag::PacketFlag::TOO_LONG); // DEBUG
|
||||
// Timeout here is normal (end of message)
|
||||
rx_state = WAITING;
|
||||
//push_packet(pocsag::PacketFlag::TIMED_OUT);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -113,12 +112,12 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
|
||||
codeword_count++;
|
||||
} else {
|
||||
push_packet(pocsag::PacketFlag::NORMAL);
|
||||
rx_state = WAITING;
|
||||
rx_state = PREAMBLE;
|
||||
sync_timeout = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Timed out (no end of message received)
|
||||
packet.set(0, codeword_count); // DEBUG
|
||||
packet.set(0, codeword_count); // Replace first codeword with count, for debug
|
||||
push_packet(pocsag::PacketFlag::TIMED_OUT);
|
||||
rx_state = WAITING;
|
||||
}
|
||||
|
364
firmware/common/bch_code.cpp
Normal file
364
firmware/common/bch_code.cpp
Normal file
@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Craig Shelley (craig@microtron.org.uk)
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* BCH Encoder/Decoder - Adapted from GNURadio
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <ch.h>
|
||||
|
||||
#include "bch_code.hpp"
|
||||
|
||||
void BCHCode::generate_gf() {
|
||||
/*
|
||||
* generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m]
|
||||
* lookup tables: index->polynomial form alpha_to[] contains j=alpha**i;
|
||||
* polynomial form -> index form index_of[j=alpha**i] = i alpha=2 is the
|
||||
* primitive element of GF(2**m)
|
||||
*/
|
||||
|
||||
int i, mask;
|
||||
mask = 1;
|
||||
alpha_to[m] = 0;
|
||||
|
||||
for (i = 0; i < m; i++)
|
||||
{
|
||||
alpha_to[i] = mask;
|
||||
|
||||
index_of[alpha_to[i]] = i;
|
||||
|
||||
if (p[i] != 0)
|
||||
alpha_to[m] ^= mask;
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
index_of[alpha_to[m]] = m;
|
||||
|
||||
mask >>= 1;
|
||||
|
||||
for (i = m + 1; i < n; i++)
|
||||
{
|
||||
if (alpha_to[i - 1] >= mask)
|
||||
alpha_to[i] = alpha_to[m] ^ ((alpha_to[i - 1] ^ mask) << 1);
|
||||
else
|
||||
alpha_to[i] = alpha_to[i - 1] << 1;
|
||||
|
||||
index_of[alpha_to[i]] = i;
|
||||
}
|
||||
|
||||
index_of[0] = -1;
|
||||
}
|
||||
|
||||
|
||||
void BCHCode::gen_poly() {
|
||||
/*
|
||||
* Compute generator polynomial of BCH code of length = 31, redundancy = 10
|
||||
* (OK, this is not very efficient, but we only do it once, right? :)
|
||||
*/
|
||||
|
||||
int ii, jj, ll, kaux;
|
||||
int test, aux, nocycles, root, noterms, rdncy;
|
||||
int cycle[15][6], size[15], min[11], zeros[11];
|
||||
|
||||
// Generate cycle sets modulo 31
|
||||
cycle[0][0] = 0; size[0] = 1;
|
||||
cycle[1][0] = 1; size[1] = 1;
|
||||
jj = 1; // cycle set index
|
||||
|
||||
do
|
||||
{
|
||||
// Generate the jj-th cycle set
|
||||
ii = 0;
|
||||
do
|
||||
{
|
||||
ii++;
|
||||
cycle[jj][ii] = (cycle[jj][ii - 1] * 2) % n;
|
||||
size[jj]++;
|
||||
aux = (cycle[jj][ii] * 2) % n;
|
||||
|
||||
} while (aux != cycle[jj][0]);
|
||||
|
||||
// Next cycle set representative
|
||||
ll = 0;
|
||||
do
|
||||
{
|
||||
ll++;
|
||||
test = 0;
|
||||
for (ii = 1; ((ii <= jj) && (!test)); ii++)
|
||||
// Examine previous cycle sets
|
||||
for (kaux = 0; ((kaux < size[ii]) && (!test)); kaux++)
|
||||
if (ll == cycle[ii][kaux])
|
||||
test = 1;
|
||||
}
|
||||
while ((test) && (ll < (n - 1)));
|
||||
|
||||
if (!(test))
|
||||
{
|
||||
jj++; // next cycle set index
|
||||
cycle[jj][0] = ll;
|
||||
size[jj] = 1;
|
||||
}
|
||||
|
||||
} while (ll < (n - 1));
|
||||
|
||||
nocycles = jj; // number of cycle sets modulo n
|
||||
// Search for roots 1, 2, ..., d-1 in cycle sets
|
||||
|
||||
kaux = 0;
|
||||
rdncy = 0;
|
||||
|
||||
for (ii = 1; ii <= nocycles; ii++)
|
||||
{
|
||||
min[kaux] = 0;
|
||||
|
||||
for (jj = 0; jj < size[ii]; jj++)
|
||||
for (root = 1; root < d; root++)
|
||||
if (root == cycle[ii][jj])
|
||||
min[kaux] = ii;
|
||||
|
||||
if (min[kaux])
|
||||
{
|
||||
rdncy += size[min[kaux]];
|
||||
kaux++;
|
||||
}
|
||||
}
|
||||
|
||||
noterms = kaux;
|
||||
kaux = 1;
|
||||
|
||||
for (ii = 0; ii < noterms; ii++)
|
||||
for (jj = 0; jj < size[min[ii]]; jj++)
|
||||
{
|
||||
zeros[kaux] = cycle[min[ii]][jj];
|
||||
kaux++;
|
||||
}
|
||||
|
||||
// Compute generator polynomial
|
||||
g[0] = alpha_to[zeros[1]];
|
||||
g[1] = 1; // g(x) = (X + zeros[1]) initially
|
||||
|
||||
for (ii = 2; ii <= rdncy; ii++)
|
||||
{
|
||||
g[ii] = 1;
|
||||
for (jj = ii - 1; jj > 0; jj--)
|
||||
if (g[jj] != 0)
|
||||
g[jj] = g[jj - 1] ^ alpha_to[(index_of[g[jj]] + zeros[ii]) % n];
|
||||
else
|
||||
g[jj] = g[jj - 1];
|
||||
|
||||
g[0] = alpha_to[(index_of[g[0]] + zeros[ii]) % n];
|
||||
}
|
||||
}
|
||||
|
||||
int * BCHCode::encode(int data[]) {
|
||||
// Calculate redundant bits bb[]
|
||||
|
||||
int h, i, j=0, start=0, end=(n-k); // 10
|
||||
int Mr[31];
|
||||
|
||||
if (!valid) return nullptr;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
Mr[i] = 0;
|
||||
}
|
||||
|
||||
for (h = 0; h < k; ++h)
|
||||
Mr[h] = data[h];
|
||||
|
||||
while (end < n)
|
||||
{
|
||||
for (i=end; i>start-2; --i)
|
||||
{
|
||||
if (Mr[start] != 0)
|
||||
{
|
||||
Mr[i]^=g[j];
|
||||
++j;
|
||||
}
|
||||
else
|
||||
{
|
||||
++start;
|
||||
j = 0;
|
||||
end = start+(n-k);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
j = 0;
|
||||
for (i = start; i<end; ++i) {
|
||||
bb[j]=Mr[i];
|
||||
++j;
|
||||
}
|
||||
|
||||
return bb;
|
||||
};
|
||||
|
||||
|
||||
int BCHCode::decode(int recd[]) {
|
||||
// We do not need the Berlekamp algorithm to decode.
|
||||
// We solve before hand two equations in two variables.
|
||||
|
||||
int i, j, q;
|
||||
int elp[3], s[5], s3;
|
||||
int count = 0, syn_error = 0;
|
||||
int loc[3], reg[3];
|
||||
int aux;
|
||||
int retval=0;
|
||||
|
||||
if (!valid) return 0;
|
||||
|
||||
for (i = 1; i <= 4; i++) {
|
||||
s[i] = 0;
|
||||
for (j = 0; j < n; j++) {
|
||||
if (recd[j] != 0) {
|
||||
s[i] ^= alpha_to[(i * j) % n];
|
||||
}
|
||||
}
|
||||
if (s[i] != 0) {
|
||||
syn_error = 1; // set flag if non-zero syndrome
|
||||
}
|
||||
/* NOTE: If only error detection is needed,
|
||||
* then exit the program here...
|
||||
*/
|
||||
// Convert syndrome from polynomial form to index form
|
||||
s[i] = index_of[s[i]];
|
||||
};
|
||||
|
||||
if (syn_error) { // If there are errors, try to correct them
|
||||
if (s[1] != -1) {
|
||||
s3 = (s[1] * 3) % n;
|
||||
if ( s[3] == s3 ) { // Was it a single error ?
|
||||
//printf("One error at %d\n", s[1]);
|
||||
recd[s[1]] ^= 1; // Yes: Correct it
|
||||
} else {
|
||||
/* Assume two errors occurred and solve
|
||||
* for the coefficients of sigma(x), the
|
||||
* error locator polynomial
|
||||
*/
|
||||
if (s[3] != -1) {
|
||||
aux = alpha_to[s3] ^ alpha_to[s[3]];
|
||||
} else {
|
||||
aux = alpha_to[s3];
|
||||
}
|
||||
elp[0] = 0;
|
||||
elp[1] = (s[2] - index_of[aux] + n) % n;
|
||||
elp[2] = (s[1] - index_of[aux] + n) % n;
|
||||
//printf("sigma(x) = ");
|
||||
//for (i = 0; i <= 2; i++) {
|
||||
// printf("%3d ", elp[i]);
|
||||
//}
|
||||
//printf("\n");
|
||||
//printf("Roots: ");
|
||||
|
||||
// Find roots of the error location polynomial
|
||||
for (i = 1; i <= 2; i++) {
|
||||
reg[i] = elp[i];
|
||||
}
|
||||
count = 0;
|
||||
for (i = 1; i <= n; i++) { // Chien search
|
||||
q = 1;
|
||||
for (j = 1; j <= 2; j++) {
|
||||
if (reg[j] != -1) {
|
||||
reg[j] = (reg[j] + j) % n;
|
||||
q ^= alpha_to[reg[j]];
|
||||
}
|
||||
}
|
||||
if (!q) { // store error location number indices
|
||||
loc[count] = i % n;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 2) {
|
||||
// no. roots = degree of elp hence 2 errors
|
||||
for (i = 0; i < 2; i++)
|
||||
recd[loc[i]] ^= 1;
|
||||
} else { // Cannot solve: Error detection
|
||||
retval=1;
|
||||
}
|
||||
}
|
||||
} else if (s[2] != -1) { // Error detection
|
||||
retval=1;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Example usage BCH(31,21,5)
|
||||
*
|
||||
* p[] = coefficients of primitive polynomial used to generate GF(2**5)
|
||||
* m = order of the field GF(2**5) = 5
|
||||
* n = 2**5 - 1 = 31
|
||||
* t = 2 = error correcting capability
|
||||
* d = 2*t + 1 = 5 = designed minimum distance
|
||||
* k = n - deg(g(x)) = 21 = dimension
|
||||
* g[] = coefficients of generator polynomial, g(x) [n - k + 1]=[11]
|
||||
* alpha_to [] = log table of GF(2**5)
|
||||
* index_of[] = antilog table of GF(2**5)
|
||||
* data[] = coefficients of data polynomial, i(x)
|
||||
* bb[] = coefficients of redundancy polynomial ( x**(10) i(x) ) modulo g(x)
|
||||
*/
|
||||
BCHCode::BCHCode(
|
||||
std::vector<int> p_init, int m, int n, int k, int t
|
||||
) : m { m },
|
||||
n { n },
|
||||
k { k },
|
||||
t { t } {
|
||||
size_t i;
|
||||
|
||||
d = 5;
|
||||
|
||||
alpha_to = (int *)chHeapAlloc(NULL, sizeof(int) * (n + 1));
|
||||
index_of = (int *)chHeapAlloc(0, sizeof(int) * (n + 1));
|
||||
p = (int *)chHeapAlloc(0, sizeof(int) * (m + 1));
|
||||
g = (int *)chHeapAlloc(0, sizeof(int) * (n - k + 1));
|
||||
bb = (int *)chHeapAlloc(0, sizeof(int) * (n - k + 1));
|
||||
|
||||
if (alpha_to == NULL ||
|
||||
index_of == NULL ||
|
||||
p == NULL ||
|
||||
g == NULL ||
|
||||
bb == NULL)
|
||||
valid = false;
|
||||
else
|
||||
valid = true;
|
||||
|
||||
if (valid) {
|
||||
for (i = 0; i < (m + 1); i++) {
|
||||
p[i] = p_init[i];
|
||||
}
|
||||
|
||||
generate_gf(); /* generate the Galois Field GF(2**m) */
|
||||
gen_poly(); /* Compute the generator polynomial of BCH code */
|
||||
}
|
||||
}
|
||||
|
||||
BCHCode::~BCHCode() {
|
||||
if (alpha_to != NULL) chHeapFree(alpha_to);
|
||||
if (index_of != NULL) chHeapFree(index_of);
|
||||
if (p != NULL) chHeapFree(p);
|
||||
if (g != NULL) chHeapFree(g);
|
||||
if (bb != NULL) chHeapFree(bb);
|
||||
}
|
61
firmware/common/bch_code.hpp
Normal file
61
firmware/common/bch_code.hpp
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Craig Shelley (craig@microtron.org.uk)
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* BCH Encoder/Decoder - Adapted from GNURadio
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BCHCODE_H__
|
||||
#define __BCHCODE_H__
|
||||
|
||||
#include <vector>
|
||||
|
||||
class BCHCode {
|
||||
public:
|
||||
BCHCode(std::vector<int> p_init, int m, int n, int k, int t);
|
||||
~BCHCode();
|
||||
|
||||
BCHCode(const BCHCode&) = delete;
|
||||
BCHCode(BCHCode&&) = delete;
|
||||
BCHCode& operator=(const BCHCode&) = delete;
|
||||
BCHCode& operator=(BCHCode&&) = delete;
|
||||
|
||||
int * encode(int data[]);
|
||||
int decode(int recd[]);
|
||||
|
||||
private:
|
||||
void gen_poly();
|
||||
void generate_gf();
|
||||
|
||||
bool valid { false };
|
||||
|
||||
int d { };
|
||||
int * p { }; // coefficients of primitive polynomial used to generate GF(2**5)
|
||||
int m { }; // order of the field GF(2**5) = 5
|
||||
int n { }; // 2**5 - 1 = 31
|
||||
int k { }; // n - deg(g(x)) = 21 = dimension
|
||||
int t { }; // 2 = error correcting capability
|
||||
int * alpha_to { }; // log table of GF(2**5)
|
||||
int * index_of { }; // antilog table of GF(2**5)
|
||||
int * g { }; // coefficients of generator polynomial, g(x) [n - k + 1]=[11]
|
||||
int * bb { }; // coefficients of redundancy polynomial ( x**(10) i(x) ) modulo g(x)
|
||||
};
|
||||
|
||||
#endif/*__BCHCODE_H__*/
|
@ -23,13 +23,10 @@
|
||||
#include "pocsag.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace pocsag {
|
||||
@ -48,12 +45,118 @@ std::string flag_str(PacketFlag packetflag) {
|
||||
case PacketFlag::NORMAL: return "OK";
|
||||
case PacketFlag::IDLE: return "IDLE";
|
||||
case PacketFlag::TIMED_OUT: return "TIMED OUT";
|
||||
case PacketFlag::TOO_LONG: return "TOO LONG";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool decode_batch(const POCSAGPacket& batch, POCSAGState * const state) {
|
||||
void insert_BCH(BCHCode& BCH_code, uint32_t * codeword) {
|
||||
uint32_t parity = 0;
|
||||
int data[21];
|
||||
int bit;
|
||||
int * bb;
|
||||
size_t c;
|
||||
|
||||
for (c = 0; c < 21; c++) {
|
||||
bit = (((*codeword) << c) & 0x80000000U) ? 1 : 0;
|
||||
if (bit) parity++;
|
||||
data[c] = bit;
|
||||
}
|
||||
|
||||
bb = BCH_code.encode(data);
|
||||
|
||||
// Make sure ECC bits are cleared
|
||||
(*codeword) &= 0xFFFFF801;
|
||||
|
||||
for (c = 0; c < 10; c++) {
|
||||
bit = bb[c];
|
||||
(*codeword) |= (bit << (10 - c));
|
||||
if (bit) parity++;
|
||||
}
|
||||
|
||||
// Even parity
|
||||
(*codeword) |= (parity & 1);
|
||||
}
|
||||
|
||||
void pocsag_encode(
|
||||
BCHCode& BCH_code, const std::string text, const uint32_t address,
|
||||
std::vector<uint32_t>& codewords) {
|
||||
|
||||
size_t b, c, address_slot;
|
||||
size_t bit_idx, char_idx = 0;
|
||||
uint32_t codeword;
|
||||
char ascii_char;
|
||||
|
||||
size_t text_size = text.size();
|
||||
|
||||
// Preamble
|
||||
for (b = 0; b < (POCSAG_PREAMBLE_LENGTH / 32); b++) {
|
||||
codewords.push_back(0xAAAAAAAA);
|
||||
}
|
||||
|
||||
// Address
|
||||
codeword = (address & 0x1FFFF8U) << 13;
|
||||
address_slot = (address & 7) * 2;
|
||||
// Function
|
||||
codeword = 3 << 11;
|
||||
|
||||
insert_BCH(BCH_code, &codeword);
|
||||
|
||||
// Address batch
|
||||
codewords.push_back(POCSAG_SYNCWORD);
|
||||
for (c = 0; c < 16; c++) {
|
||||
if (c == address_slot)
|
||||
codewords.push_back(codeword);
|
||||
else
|
||||
codewords.push_back(POCSAG_IDLEWORD);
|
||||
}
|
||||
|
||||
codeword = 0;
|
||||
bit_idx = 20 + 11;
|
||||
|
||||
// Messages batch(es)
|
||||
do {
|
||||
codewords.push_back(POCSAG_SYNCWORD);
|
||||
|
||||
for (c = 0; c < 16; c++) {
|
||||
// Fill up 20 bits
|
||||
do {
|
||||
bit_idx -= 7;
|
||||
|
||||
if (char_idx < text_size)
|
||||
ascii_char = text[char_idx] & 0x7F;
|
||||
else
|
||||
ascii_char = 0; // Padding
|
||||
|
||||
// Bottom's up
|
||||
ascii_char = (ascii_char & 0xF0) >> 4 | (ascii_char & 0x0F) << 4; // *6543210 -> 3210*654
|
||||
ascii_char = (ascii_char & 0xCC) >> 2 | (ascii_char & 0x33) << 2; // 3210*654 -> 103254*6
|
||||
ascii_char = (ascii_char & 0xAA) >> 2 | (ascii_char & 0x55); // 103254*6 -> *0123456
|
||||
|
||||
codeword |= (ascii_char << bit_idx);
|
||||
|
||||
char_idx++;
|
||||
|
||||
} while (bit_idx > 11);
|
||||
|
||||
codeword &= 0x7FFFF800; // Trim data
|
||||
codeword |= 0x80000000; // Message type
|
||||
insert_BCH(BCH_code, &codeword);
|
||||
|
||||
codewords.push_back(codeword);
|
||||
|
||||
if (bit_idx != 11) {
|
||||
bit_idx = 20 + bit_idx;
|
||||
codeword = ascii_char << bit_idx;
|
||||
} else {
|
||||
bit_idx = 20 + 11;
|
||||
codeword = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} while (char_idx < text_size);
|
||||
}
|
||||
|
||||
bool pocsag_decode_batch(const POCSAGPacket& batch, POCSAGState * const state) {
|
||||
uint32_t codeword;
|
||||
char ascii_char;
|
||||
std::string output_text = "";
|
||||
@ -67,7 +170,7 @@ bool decode_batch(const POCSAGPacket& batch, POCSAGState * const state) {
|
||||
if (!(codeword & 0x80000000U)) {
|
||||
// Address codeword
|
||||
if (state->mode == STATE_CLEAR) {
|
||||
if (codeword != POCSAG_IDLE) {
|
||||
if (codeword != POCSAG_IDLEWORD) {
|
||||
state->function = (codeword >> 11) & 3;
|
||||
state->address = (codeword >> 10) & 0x1FFFF8U; // 18 MSBs are transmitted
|
||||
state->mode = STATE_HAVE_ADDRESS;
|
||||
|
@ -23,16 +23,20 @@
|
||||
#ifndef __POCSAG_H__
|
||||
#define __POCSAG_H__
|
||||
|
||||
#define POCSAG_PREAMBLE_LENGTH 576
|
||||
#define POCSAG_TIMEOUT (576 * 2) // Preamble length * 2
|
||||
#define POCSAG_SYNC 0x7CD215D8
|
||||
#define POCSAG_IDLE 0x7A89C197
|
||||
#define POCSAG_SYNCWORD 0x7CD215D8
|
||||
#define POCSAG_IDLEWORD 0x7A89C197
|
||||
#define POCSAG_AUDIO_RATE 24000
|
||||
#define POCSAG_BATCH_LENGTH (17 * 32)
|
||||
|
||||
#include "pocsag_packet.hpp"
|
||||
#include "bch_code.hpp"
|
||||
|
||||
namespace pocsag {
|
||||
|
||||
// Todo: these enums suck, make a better decode_batch
|
||||
|
||||
enum Mode : uint32_t {
|
||||
STATE_CLEAR,
|
||||
STATE_HAVE_ADDRESS,
|
||||
@ -58,7 +62,9 @@ struct POCSAGState {
|
||||
std::string bitrate_str(BitRate bitrate);
|
||||
std::string flag_str(PacketFlag packetflag);
|
||||
|
||||
bool decode_batch(const POCSAGPacket& batch, POCSAGState * const state);
|
||||
void insert_BCH(BCHCode& BCH_code, uint32_t * codeword);
|
||||
void pocsag_encode(BCHCode& BCH_code, const std::string text, const uint32_t address, std::vector<uint32_t>& codewords);
|
||||
bool pocsag_decode_batch(const POCSAGPacket& batch, POCSAGState * const state);
|
||||
|
||||
} /* namespace pocsag */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user