From 82ff45860e136fcfc8270585a0eabd3dddfb1fa2 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 1 Dec 2015 11:24:48 -0800 Subject: [PATCH] Add basic ERT (OOK) utility meter reception. Extracted the Manchester formatting function for use in ERT, now also returns an indication of bits in error. --- firmware/application/receiver_model.cpp | 3 +- firmware/application/ui_receiver.cpp | 84 +++++++++++++++-- firmware/application/ui_receiver.hpp | 2 + firmware/baseband/Makefile | 1 + firmware/baseband/main.cpp | 5 + firmware/baseband/proc_ert.cpp | 117 ++++++++++++++++++++++++ firmware/baseband/proc_ert.hpp | 108 ++++++++++++++++++++++ firmware/common/message.hpp | 17 ++++ 8 files changed, 329 insertions(+), 8 deletions(-) create mode 100644 firmware/baseband/proc_ert.cpp create mode 100644 firmware/baseband/proc_ert.hpp diff --git a/firmware/application/receiver_model.cpp b/firmware/application/receiver_model.cpp index e1914ba9..709fbd9b 100644 --- a/firmware/application/receiver_model.cpp +++ b/firmware/application/receiver_model.cpp @@ -138,7 +138,8 @@ void ReceiverModel::disable() { } int32_t ReceiverModel::tuning_offset() { - if( baseband_configuration.mode == 4 ) { + if( (baseband_configuration.mode == 4) || + (baseband_configuration.mode == 6) ) { return 0; } else { return -(sampling_rate() / 4); diff --git a/firmware/application/ui_receiver.cpp b/firmware/application/ui_receiver.cpp index 35815e56..b1a45356 100644 --- a/firmware/application/ui_receiver.cpp +++ b/firmware/application/ui_receiver.cpp @@ -512,6 +512,12 @@ void ReceiverView::on_show() { this->on_packet_tpms(*message); } ); + message_map.register_handler(Message::ID::ERTPacket, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_packet_ert(*message); + } + ); sd_card_status_signal_token = sd_card::status_signal += [this](const sd_card::Status status) { this->on_sd_card_status(status); @@ -522,6 +528,7 @@ void ReceiverView::on_hide() { sd_card::status_signal -= sd_card_status_signal_token; auto& message_map = context().message_map(); + message_map.unregister_handler(Message::ID::ERTPacket); message_map.unregister_handler(Message::ID::TPMSPacket); message_map.unregister_handler(Message::ID::AISPacket); } @@ -537,16 +544,21 @@ void ReceiverView::on_packet_ais(const AISPacketMessage& message) { static FIL fil_tpms; -void ReceiverView::on_packet_tpms(const TPMSPacketMessage& message) { - auto payload = message.packet.payload; - auto payload_length = message.packet.bits_received; +static std::pair format_manchester( + const uint_fast8_t sense, + const std::bitset<1024>& payload, + const size_t payload_length +) { + const size_t payload_length_decoded = payload_length / 2; + const size_t payload_length_bytes = (payload_length_decoded + 7) / 8; + const size_t payload_length_symbols_rounded = payload_length_bytes * 8 * 2; std::string hex_data; std::string hex_error; uint8_t byte_data = 0; uint8_t byte_error = 0; - for(size_t i=0; i(widget_content.get()); - console->writeln(hex_data.substr(0, 240 / 8)); + console->writeln(hex_formatted.first.substr(0, 240 / 8)); if( !f_error(&fil_tpms) ) { rtc::RTC datetime; @@ -579,12 +600,51 @@ void ReceiverView::on_packet_tpms(const TPMSPacketMessage& message) { // TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue! const auto tuning_frequency_str = to_string_dec_uint(tuning_frequency, 10); - std::string log = timestamp + " " + tuning_frequency_str + " FSK 38.4 19.2 " + hex_data + "/" + hex_error + "\r\n"; + std::string log = timestamp + " " + tuning_frequency_str + " FSK 38.4 19.2 " + hex_formatted.first + "/" + hex_formatted.second + "\r\n"; f_puts(log.c_str(), &fil_tpms); f_sync(&fil_tpms); } } +static std::bitset<512> manchester_decode( + const size_t sense, + const std::bitset<1024>& encoded, + const size_t count +) { + std::bitset<512> result; + for(size_t i=0; i> 1] = encoded[i+sense]; + } + return result; +} + +static std::bitset<512> manchester_errors( + const std::bitset<1024>& encoded, + const size_t count +) { + std::bitset<512> result; + for(size_t i=0; i> 1] = (encoded[i+0] == encoded[i+1]); + } + return result; +} + +void ReceiverView::on_packet_ert(const ERTPacketMessage& message) { + auto console = reinterpret_cast(widget_content.get()); + + if( message.packet.preamble == 0x555516a3 ) { + console->writeln("IDM"); + } + if( message.packet.preamble == 0x1f2a60 ) { + console->writeln("SCM"); + const auto decoded = manchester_decode(0, message.packet.payload, message.packet.bits_received); + } + + const auto hex_formatted = format_manchester(0, message.packet.payload, message.packet.bits_received); + console->writeln(hex_formatted.first); + console->writeln(hex_formatted.second); +} + void ReceiverView::on_sd_card_status(const sd_card::Status status) { if( status == sd_card::Status::Mounted ) { const auto open_result = f_open(&fil_tpms, "tpms.txt", FA_WRITE | FA_OPEN_ALWAYS); @@ -642,6 +702,15 @@ void ReceiverView::on_modulation_changed(int32_t modulation) { receiver_model.set_baseband_bandwidth(1750000); break; + case 6: + receiver_model.set_baseband_configuration({ + .mode = modulation, + .sampling_rate = 4194304, + .decimation_factor = 1, + }); + receiver_model.set_baseband_bandwidth(1750000); + break; + case 4: receiver_model.set_baseband_configuration({ .mode = modulation, @@ -667,6 +736,7 @@ void ReceiverView::on_modulation_changed(int32_t modulation) { switch(modulation) { case 3: case 5: + case 6: widget_content = std::make_unique(); add_child(widget_content.get()); break; diff --git a/firmware/application/ui_receiver.hpp b/firmware/application/ui_receiver.hpp index 8ff647e3..65e97f04 100644 --- a/firmware/application/ui_receiver.hpp +++ b/firmware/application/ui_receiver.hpp @@ -417,6 +417,7 @@ private: { "WFM ", 2 }, { "AIS ", 3 }, { "TPMS", 5 }, + { "ERT", 6 }, { "SPEC", 4 }, } }; @@ -475,6 +476,7 @@ private: void on_packet_ais(const AISPacketMessage& message); void on_packet_tpms(const TPMSPacketMessage& message); + void on_packet_ert(const ERTPacketMessage& message); void on_sd_card_status(const sd_card::Status status); SignalToken sd_card_status_signal_token; diff --git a/firmware/baseband/Makefile b/firmware/baseband/Makefile index ffb20d12..8e930720 100755 --- a/firmware/baseband/Makefile +++ b/firmware/baseband/Makefile @@ -140,6 +140,7 @@ CPPSRC = main.cpp \ proc_ais.cpp \ proc_wideband_spectrum.cpp \ proc_tpms.cpp \ + proc_ert.cpp \ dsp_squelch.cpp \ clock_recovery.cpp \ packet_builder.cpp \ diff --git a/firmware/baseband/main.cpp b/firmware/baseband/main.cpp index 2a957ff6..70a9d43e 100755 --- a/firmware/baseband/main.cpp +++ b/firmware/baseband/main.cpp @@ -58,6 +58,7 @@ #include "proc_ais.hpp" #include "proc_wideband_spectrum.hpp" #include "proc_tpms.hpp" +#include "proc_ert.hpp" #include "clock_recovery.hpp" #include "packet_builder.hpp" @@ -370,6 +371,10 @@ int main(void) { baseband_thread.baseband_processor = new TPMSProcessor(); break; + case 6: + baseband_thread.baseband_processor = new ERTProcessor(); + break; + default: break; } diff --git a/firmware/baseband/proc_ert.cpp b/firmware/baseband/proc_ert.cpp new file mode 100644 index 00000000..f27396c2 --- /dev/null +++ b/firmware/baseband/proc_ert.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * 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 "proc_ert.hpp" + +#include "portapack_shared_memory.hpp" + +#include "i2s.hpp" +using namespace lpc43xx; + +float ERTProcessor::abs(const complex8_t& v) { + // const int16_t r = v.real() - offset_i; + // const int16_t i = v.imag() - offset_q; + // const uint32_t r2 = r * r; + // const uint32_t i2 = i * i; + // const uint32_t r2_i2 = r2 + i2; + // return std::sqrt(static_cast(r2_i2)); + const float r = static_cast(v.real()) - offset_i; + const float i = static_cast(v.imag()) - offset_q; + const float r2 = r * r; + const float i2 = i * i; + const float r2_i2 = r2 + i2; + return std::sqrt(r2_i2); +} + +void ERTProcessor::execute(buffer_c8_t buffer) { + /* 4.194304MHz, 2048 samples */ + // auto decimator_out = decimator.execute(buffer); + + const complex8_t* src = &buffer.p[0]; + const complex8_t* const src_end = &buffer.p[buffer.count]; + + average_i += src->real(); + average_q += src->imag(); + average_count++; + if( average_count == 2048 ) { + offset_i = static_cast(average_i) / 2048.0f; + offset_q = static_cast(average_q) / 2048.0f; + average_i = 0; + average_q = 0; + average_count = 0; + } + + const float gain = 128 * samples_per_symbol; + const float k = 1.0f / gain; + + while(src < src_end) { + float sum = 0.0f; + for(size_t i=0; i<(samples_per_symbol / 2); i++) { + sum += abs(*(src++)); + } + sum_half_period[1] = sum_half_period[0]; + sum_half_period[0] = sum; + + sum_period[2] = sum_period[1]; + sum_period[1] = sum_period[0]; + sum_period[0] = (sum_half_period[0] + sum_half_period[1]) * k; + + manchester[2] = manchester[1]; + manchester[1] = manchester[0]; + manchester[0] = sum_period[2] - sum_period[0]; + + const auto data = manchester[0] - manchester[2]; + + clock_recovery(data); + } + + i2s::i2s0::tx_mute(); +} + +void ERTProcessor::consume_symbol( + const float raw_symbol +) { + const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; + scm_builder.execute(sliced_symbol); + idm_builder.execute(sliced_symbol); +} + +void ERTProcessor::scm_handler( + const std::bitset<1024>& payload, + const size_t bits_received +) { + ERTPacketMessage message; + message.packet.preamble = 0x1f2a60; + message.packet.payload = payload; + message.packet.bits_received = bits_received; + shared_memory.application_queue.push(message); +} + +void ERTProcessor::idm_handler( + const std::bitset<1024>& payload, + const size_t bits_received +) { + ERTPacketMessage message; + message.packet.preamble = 0x555516a3; + message.packet.payload = payload; + message.packet.bits_received = bits_received; + shared_memory.application_queue.push(message); +} diff --git a/firmware/baseband/proc_ert.hpp b/firmware/baseband/proc_ert.hpp new file mode 100644 index 00000000..bc5fcc93 --- /dev/null +++ b/firmware/baseband/proc_ert.hpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * 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 __PROC_ERT_H__ +#define __PROC_ERT_H__ + +#include "baseband_processor.hpp" + +#include "channel_decimator.hpp" + +#include "clock_recovery.hpp" +#include "symbol_coding.hpp" +#include "packet_builder.hpp" + +#include "message.hpp" + +#include +#include +#include + +// ''.join(['%d%d' % (c, 1-c) for c in map(int, bin(0x1f2a60)[2:].zfill(21))]) +constexpr uint64_t scm_preamble_and_sync_manchester { 0b101010101001011001100110010110100101010101 }; +constexpr size_t scm_preamble_and_sync_length { 42 - 10 }; +constexpr size_t scm_payload_length_max { 150 }; + +// ''.join(['%d%d' % (c, 1-c) for c in map(int, bin(0x555516a3)[2:].zfill(32))]) +constexpr uint64_t idm_preamble_and_sync_manchester { 0b0110011001100110011001100110011001010110011010011001100101011010 }; +constexpr size_t idm_preamble_and_sync_length { 64 - 16 }; + +// TODO: Should be 1408, but need to widen pathway to application processor... +constexpr size_t idm_payload_length_max { 960 }; + +class ERTProcessor : public BasebandProcessor { +public: + using payload_t = std::bitset<1024>; + + void execute(buffer_c8_t buffer) override; + +private: + const uint32_t baseband_sampling_rate = 4194304; + const size_t decimation = 1; + const uint32_t symbol_rate = 32768; + + const uint32_t channel_sampling_rate = baseband_sampling_rate / decimation; + const size_t samples_per_symbol = channel_sampling_rate / symbol_rate; + const uint32_t clock_recovery_rate = symbol_rate * 2; + + // ChannelDecimator decimator { ChannelDecimator::DecimationFactor::By2, false }; + + clock_recovery::ClockRecovery clock_recovery { + clock_recovery_rate, symbol_rate, { 1.0f / 18.0f }, + [this](const float symbol) { this->consume_symbol(symbol); } + }; + + PacketBuilder scm_builder { + { scm_preamble_and_sync_manchester, scm_preamble_and_sync_length, 1 }, + { }, + { scm_payload_length_max }, + [this](const payload_t& payload, const size_t bits_received) { + this->scm_handler(payload, bits_received); + } + }; + + PacketBuilder idm_builder { + { idm_preamble_and_sync_manchester, idm_preamble_and_sync_length, 1 }, + { }, + { idm_payload_length_max }, + [this](const payload_t& payload, const size_t bits_received) { + this->idm_handler(payload, bits_received); + } + }; + + void consume_symbol(const float symbol); + void scm_handler(const payload_t& payload, const size_t bits_received); + void idm_handler(const payload_t& payload, const size_t bits_received); + + float sum_half_period[2]; + float sum_period[3]; + float manchester[3]; + + int32_t average_i { 0 }; + int32_t average_q { 0 }; + size_t average_count { 0 }; + float offset_i { 0.0f }; + float offset_q { 0.0f }; + + float abs(const complex8_t& v); +}; + +#endif/*__PROC_ERT_H__*/ diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 9a290144..d5c0101b 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -46,6 +46,7 @@ public: TPMSPacket = 6, Shutdown = 8, AISPacket = 7, + ERTPacket = 9, MAX }; @@ -240,6 +241,22 @@ public: } }; +struct ERTPacket { + uint64_t preamble { 0 }; + std::bitset<1024> payload; + size_t bits_received { 0 }; +}; + +class ERTPacketMessage : public Message { +public: + constexpr ERTPacketMessage( + ) : Message { ID::ERTPacket } + { + } + + ERTPacket packet; +}; + class MessageHandlerMap { public: using MessageHandler = std::function;