From 24abe4b42725c75e99ecf3852ba0abe24512e251 Mon Sep 17 00:00:00 2001 From: furrtek Date: Mon, 6 Feb 2017 20:32:33 +0000 Subject: [PATCH] Yet another POCSAG bugfix (multi-batch messages are not cut anymore) Added BCH ECC functions for checking, error correction and encoding --- firmware/application/CMakeLists.txt | 7 +- firmware/application/Makefile | 30 +++ firmware/application/pocsag_app.cpp | 71 +++--- firmware/application/pocsag_app.hpp | 12 +- firmware/baseband/proc_pocsag.cpp | 19 +- firmware/common/bch_code.cpp | 364 ++++++++++++++++++++++++++++ firmware/common/bch_code.hpp | 61 +++++ firmware/common/pocsag.cpp | 115 ++++++++- firmware/common/pocsag.hpp | 12 +- 9 files changed, 627 insertions(+), 64 deletions(-) create mode 100644 firmware/common/bch_code.cpp create mode 100644 firmware/common/bch_code.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index c5edb99b..37f158be 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -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 diff --git a/firmware/application/Makefile b/firmware/application/Makefile index a3a6ee38..75488f89 100644 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -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" diff --git a/firmware/application/pocsag_app.cpp b/firmware/application/pocsag_app.cpp index e70da2b5..cac8335c 100644 --- a/firmware/application/pocsag_app.cpp +++ b/firmware/application/pocsag_app.cpp @@ -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(); 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); + } } } diff --git a/firmware/application/pocsag_app.hpp b/firmware/application/pocsag_app.hpp index b615d663..a49ad8e6 100644 --- a/firmware/application/pocsag_app.hpp +++ b/firmware/application/pocsag_app.hpp @@ -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 { }; diff --git a/firmware/baseband/proc_pocsag.cpp b/firmware/baseband/proc_pocsag.cpp index 4040e3b9..4b5ee69b 100644 --- a/firmware/baseband/proc_pocsag.cpp +++ b/firmware/baseband/proc_pocsag.cpp @@ -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; } diff --git a/firmware/common/bch_code.cpp b/firmware/common/bch_code.cpp new file mode 100644 index 00000000..cf1f895d --- /dev/null +++ b/firmware/common/bch_code.cpp @@ -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 +#include +#include + +#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 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); +} diff --git a/firmware/common/bch_code.hpp b/firmware/common/bch_code.hpp new file mode 100644 index 00000000..9145ed35 --- /dev/null +++ b/firmware/common/bch_code.hpp @@ -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 + +class BCHCode { +public: + BCHCode(std::vector 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__*/ diff --git a/firmware/common/pocsag.cpp b/firmware/common/pocsag.cpp index 64ebda19..14a19578 100644 --- a/firmware/common/pocsag.cpp +++ b/firmware/common/pocsag.cpp @@ -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& 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; diff --git a/firmware/common/pocsag.hpp b/firmware/common/pocsag.hpp index 7dac95f0..a6ecafa6 100644 --- a/firmware/common/pocsag.hpp +++ b/firmware/common/pocsag.hpp @@ -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& codewords); +bool pocsag_decode_batch(const POCSAGPacket& batch, POCSAGState * const state); } /* namespace pocsag */