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:
furrtek 2017-02-06 20:32:33 +00:00
parent 98f89a84bb
commit 24abe4b427
9 changed files with 627 additions and 64 deletions

View File

@ -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

View File

@ -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"

View File

@ -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);
}
}
}

View File

@ -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 { };

View File

@ -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;
}

View 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);
}

View 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__*/

View File

@ -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;

View File

@ -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 */