diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 876caee7..254ed819 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -109,6 +109,7 @@ set(CSRC # setting. set(CPPSRC main.cpp + ${COMMON}/acars_packet.cpp ${COMMON}/adsb.cpp ${COMMON}/adsb_frame.cpp ${COMMON}/ais_baseband.cpp @@ -245,6 +246,7 @@ set(CPPSRC apps/ui_touchtunes.cpp apps/ui_view_wav.cpp apps/ui_whipcalc.cpp + apps/acars_app.cpp apps/analog_audio_app.cpp apps/ais_app.cpp apps/tpms_app.cpp diff --git a/firmware/application/apps/acars_app.cpp b/firmware/application/apps/acars_app.cpp new file mode 100644 index 00000000..3dc54bf9 --- /dev/null +++ b/firmware/application/apps/acars_app.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek + * + * 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 "acars_app.hpp" + +#include "baseband_api.hpp" +#include "portapack_persistent_memory.hpp" + +using namespace portapack; +using namespace acars; + +#include "string_format.hpp" +#include "utility.hpp" + +void ACARSLogger::log_raw_data(const acars::Packet& packet, const uint32_t frequency) { + std::string entry = "Raw: F:" + to_string_dec_uint(frequency) + "Hz "; + + // Raw hex dump of all the bytes + for (size_t c = 0; c < packet.length(); c += 8) + entry += to_string_hex(packet.read(c, 8), 8) + " "; + + log_file.write_entry(packet.received_at(), entry); +} + +/*void ACARSLogger::log_decoded( + const acars::Packet& packet, + const std::string text) { + + log_file.write_entry(packet.timestamp(), text); +}*/ + +namespace ui { + +void ACARSAppView::update_freq(rf::Frequency f) { + set_target_frequency(f); + portapack::persistent_memory::set_tuned_frequency(f); // Maybe not ? +} + +ACARSAppView::ACARSAppView(NavigationView& nav) { + baseband::run_image(portapack::spi_flash::image_tag_acars); + + add_children({ + &rssi, + &channel, + &field_rf_amp, + &field_lna, + &field_vga, + &field_frequency, + &check_log, + &console + }); + + receiver_model.set_sampling_rate(2457600); + receiver_model.set_baseband_bandwidth(1750000); + receiver_model.enable(); + + field_frequency.set_value(receiver_model.tuning_frequency()); + update_freq(receiver_model.tuning_frequency()); + field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.on_change = [this](rf::Frequency f) { + update_freq(f); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + update_freq(f); + field_frequency.set_value(f); + }; + }; + + check_log.set_value(logging); + check_log.on_select = [this](Checkbox&, bool v) { + logging = v; + }; + + logger = std::make_unique(); + if (logger) + logger->append("acars.txt"); +} + +ACARSAppView::~ACARSAppView() { + receiver_model.disable(); + baseband::shutdown(); +} + +void ACARSAppView::focus() { + field_frequency.focus(); +} + +// Useless ? +void ACARSAppView::set_parent_rect(const Rect new_parent_rect) { + View::set_parent_rect(new_parent_rect); +} + +void ACARSAppView::on_packet(const acars::Packet& packet) { + std::string alphanum_text = ""; + + if (!packet.is_valid()) + console.writeln("\n\x1B\x0INVALID PACKET"); + else { + std::string console_info; + + console_info = "\n" + to_string_datetime(packet.received_at(), HM); + console_info += " REG:" + packet.registration_number(); + + console.write(console_info); + } + + // Log raw data whatever it contains + if (logger && logging) + logger->log_raw_data(packet, target_frequency()); +} + +void ACARSAppView::set_target_frequency(const uint32_t new_value) { + target_frequency_ = new_value; + receiver_model.set_tuning_frequency(new_value); +} + +uint32_t ACARSAppView::target_frequency() const { + return target_frequency_; +} + +} /* namespace ui */ diff --git a/firmware/application/apps/acars_app.hpp b/firmware/application/apps/acars_app.hpp new file mode 100644 index 00000000..d6e5f2e7 --- /dev/null +++ b/firmware/application/apps/acars_app.hpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek + * + * 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 __ACARS_APP_H__ +#define __ACARS_APP_H__ + +#include "ui_widget.hpp" +#include "ui_receiver.hpp" +#include "ui_rssi.hpp" + +#include "log_file.hpp" + +#include "acars_packet.hpp" + +class ACARSLogger { +public: + Optional append(const std::string& filename) { + return log_file.append(filename); + } + + void log_raw_data(const acars::Packet& packet, const uint32_t frequency); + //void log_decoded(const acars::Packet& packet, const std::string text); + +private: + LogFile log_file { }; +}; + +namespace ui { + +class ACARSAppView : public View { +public: + ACARSAppView(NavigationView& nav); + ~ACARSAppView(); + + void set_parent_rect(const Rect new_parent_rect) override; + void focus() override; + + std::string title() const override { return "ACARS RX"; }; + +private: + bool logging { false }; + + RFAmpField field_rf_amp { + { 13 * 8, 0 * 16 } + }; + LNAGainField field_lna { + { 15 * 8, 0 * 16 } + }; + VGAGainField field_vga { + { 18 * 8, 0 * 16 } + }; + RSSI rssi { + { 21 * 8, 0, 6 * 8, 4 }, + }; + Channel channel { + { 21 * 8, 5, 6 * 8, 4 }, + }; + + FrequencyField field_frequency { + { 0 * 8, 0 * 8 }, + }; + Checkbox check_log { + { 22 * 8, 21 }, + 3, + "LOG", + true + }; + + Console console { + { 0, 4 * 16, 240, 240 } + }; + + std::unique_ptr logger { }; + + uint32_t target_frequency_ { }; + + void update_freq(rf::Frequency f); + + void on_packet(const acars::Packet& packet); + + uint32_t target_frequency() const; + void set_target_frequency(const uint32_t new_value); + + MessageHandlerRegistration message_handler_packet { + Message::ID::ACARSPacket, + [this](Message* const p) { + const auto message = static_cast(p); + const acars::Packet packet { message->packet }; + this->on_packet(packet); + } + }; + +}; + +} /* namespace ui */ + +#endif/*__ACARS_APP_H__*/ diff --git a/firmware/application/apps/pocsag_app.cpp b/firmware/application/apps/pocsag_app.cpp index 6808e631..f0385d2d 100644 --- a/firmware/application/apps/pocsag_app.cpp +++ b/firmware/application/apps/pocsag_app.cpp @@ -25,8 +25,6 @@ #include "baseband_api.hpp" #include "portapack_persistent_memory.hpp" -#include "pocsag.hpp" - using namespace portapack; using namespace pocsag; diff --git a/firmware/application/apps/ui_freqman.cpp b/firmware/application/apps/ui_freqman.cpp index 1b6014c8..9587d338 100644 --- a/firmware/application/apps/ui_freqman.cpp +++ b/firmware/application/apps/ui_freqman.cpp @@ -112,7 +112,7 @@ void FreqManBaseView::refresh_list() { for (size_t n = 0; n < database.size(); n++) { menu_view.add_item({ - freqman_item_string(database[n], 26), + freqman_item_string(database[n], 30), ui::Color::white(), nullptr, [this](){ @@ -212,7 +212,7 @@ FrequencyLoadView::FrequencyLoadView( }); // Resize menu view to fill screen - menu_view.set_parent_rect({ 0, 3 * 8, 240, 29 * 8 }); + menu_view.set_parent_rect({ 0, 3 * 8, 240, 30 * 8 }); // Just to allow exit on left menu_view.on_left = [&nav, this]() { diff --git a/firmware/application/apps/ui_freqman.hpp b/firmware/application/apps/ui_freqman.hpp index 7dd05d2e..ef565cdb 100644 --- a/firmware/application/apps/ui_freqman.hpp +++ b/firmware/application/apps/ui_freqman.hpp @@ -39,8 +39,6 @@ public: void focus() override; - std::string title() const override { return "Freq. manager"; }; - protected: using option_t = std::pair; using options_t = std::vector; @@ -89,6 +87,8 @@ class FrequencySaveView : public FreqManBaseView { public: FrequencySaveView(NavigationView& nav, const rf::Frequency value); + std::string title() const override { return "Save frequency"; }; + private: std::string desc_buffer { }; rf::Frequency value_ { }; @@ -126,6 +126,8 @@ public: FrequencyLoadView(NavigationView& nav); + std::string title() const override { return "Load frequency"; }; + private: void refresh_widgets(const bool v); }; @@ -135,6 +137,8 @@ public: FrequencyManagerView(NavigationView& nav); ~FrequencyManagerView(); + std::string title() const override { return "Freq. manager"; }; + private: std::string desc_buffer { }; diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index fbf14660..73b980aa 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -43,12 +43,14 @@ //GLITCH: Start of tx using ReplayThread plays a small bit of previous transmission (content of 1 buffer ?) // See fifo.reset_in() ? -//TODO: Increase resolution of audio FFT view ? Currently 48k/(256/2) (375Hz) because of the use of real values (half of FFT output) -//TODO: DCS decoder -//TODO: Make CTCSS display only when squelch is opened +//TODO: Add larger description text field in frequency load, under menuview +//TODO: Allow apps to select a preferred FREQMAN file //TODO: Make play button larger in Replay -//TODO: Put LNA and VGA controls in Soundboard //TODO: Add default headphones volume setting in Audio settings +//TODO: Put LNA and VGA controls in Soundboard +//TODO: Make CTCSS display only when squelch is opened +//TODO: DCS decoder +//TODO: Increase resolution of audio FFT view ? Currently 48k/(256/2) (375Hz) because of the use of real values (half of FFT output) //TODO: Move Touchtunes remote to Custom remote //TODO: Use escapes \x1B to set colors in text, it works ! //TODO: Open files in File Manager diff --git a/firmware/application/ui/ui_menu.cpp b/firmware/application/ui/ui_menu.cpp index 6319298b..cac18c04 100644 --- a/firmware/application/ui/ui_menu.cpp +++ b/firmware/application/ui/ui_menu.cpp @@ -42,7 +42,7 @@ void MenuItemView::unhighlight() { } void MenuItemView::paint(Painter& painter) { - Coord offset_x; + Coord offset_x { }; if (!item) return; @@ -71,7 +71,7 @@ void MenuItemView::paint(Painter& painter) { ); offset_x = 26; } else - offset_x = 8; + offset_x = 0; Style text_style { .font = paint_style.font, diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 0f4a0de5..cd4841d6 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -66,13 +66,14 @@ #include "ui_view_wav.hpp" #include "ui_whipcalc.hpp" -#include "analog_audio_app.hpp" +#include "acars_app.hpp" #include "ais_app.hpp" -#include "ert_app.hpp" -#include "tpms_app.hpp" -#include "pocsag_app.hpp" +#include "analog_audio_app.hpp" #include "capture_app.hpp" +#include "ert_app.hpp" +#include "pocsag_app.hpp" #include "replay_app.hpp" +#include "tpms_app.hpp" #include "core_control.hpp" @@ -330,19 +331,20 @@ void NavigationView::focus() { ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { add_items({ { "ADS-B: Planes", ui::Color::green(), &bitmap_icon_adsb, [&nav](){ nav.replace(); }, }, + { "ACARS: Planes", ui::Color::yellow(),&bitmap_icon_adsb, [&nav](){ nav.replace(); }, }, { "AIS: Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.replace(); } }, { "AFSK", ui::Color::yellow(),&bitmap_icon_receivers, [&nav](){ nav.replace(); } }, - { "APRS", ui::Color::grey(), &bitmap_icon_aprs, [&nav](){ nav.replace(); } }, { "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.replace(false); } }, - { "DMR framing", ui::Color::grey(), &bitmap_icon_dmr, [&nav](){ nav.replace(); } }, { "ERT: Utility Meters", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.replace(); } }, { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.replace(); } }, + { "Radiosondes", ui::Color::yellow(),&bitmap_icon_sonde, [&nav](){ nav.replace(); } }, + { "TPMS: Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.replace(); } }, + { "APRS", ui::Color::grey(), &bitmap_icon_aprs, [&nav](){ nav.replace(); } }, + { "DMR framing", ui::Color::grey(), &bitmap_icon_dmr, [&nav](){ nav.replace(); } }, { "SIGFOX", ui::Color::grey(), &bitmap_icon_fox, [&nav](){ nav.replace(); } }, // SIGFRXView { "LoRa", ui::Color::grey(), &bitmap_icon_lora, [&nav](){ nav.replace(); } }, - { "Radiosondes", ui::Color::yellow(),&bitmap_icon_sonde, [&nav](){ nav.replace(); } }, { "SSTV", ui::Color::grey(), &bitmap_icon_sstv, [&nav](){ nav.replace(); } }, { "TETRA framing", ui::Color::grey(), &bitmap_icon_tetra, [&nav](){ nav.replace(); } }, - { "TPMS: Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.replace(); } }, }); on_left = [&nav](){ nav.pop(); }; @@ -356,7 +358,6 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { { "ADS-B Mode S", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push(); } }, { "APRS", ui::Color::orange(), &bitmap_icon_aprs, [&nav](){ nav.push(); } }, { "BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav](){ nav.push(); } }, - { "Custom remote", ui::Color::grey(), &bitmap_icon_remote, [&nav](){ nav.push(); } }, { "Jammer", ui::Color::yellow(), &bitmap_icon_jammer, [&nav](){ nav.push(); } }, { "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push(); } }, { "Microphone", ui::Color::green(), &bitmap_icon_microphone, [&nav](){ nav.push(); } }, @@ -369,7 +370,8 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { { "Soundboard", ui::Color::green(), &bitmap_icon_soundboard, [&nav](){ nav.push(); } }, { "SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav](){ nav.push(); } }, { "TEDI/LCR AFSK", ui::Color::yellow(), &bitmap_icon_lcr, [&nav](){ nav.push(); } }, - { "TouchTunes remote", ui::Color::yellow(), nullptr, [&nav](){ nav.push(); } }, + { "TouchTunes remote", ui::Color::yellow(), &bitmap_icon_remote [&nav](){ nav.push(); } }, + { "Custom remote", ui::Color::grey(), &bitmap_icon_remote, [&nav](){ nav.push(); } }, }); on_left = [&nav](){ nav.pop(); }; } diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index 13f0a62f..94311c06 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -277,6 +277,13 @@ macro(DeclareTargets chunk_tag name) set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img) endmacro() +### ACARS RX + +set(MODE_CPPSRC + proc_acars.cpp +) +DeclareTargets(PACA acars) + ### ADS-B RX set(MODE_CPPSRC diff --git a/firmware/baseband/proc_acars.cpp b/firmware/baseband/proc_acars.cpp new file mode 100644 index 00000000..e0077f8e --- /dev/null +++ b/firmware/baseband/proc_acars.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek + * + * 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_acars.hpp" + +#include "portapack_shared_memory.hpp" + +#include "dsp_fir_taps.hpp" + +#include "event_m4.hpp" + +ACARSProcessor::ACARSProcessor() { + decim_0.configure(taps_11k0_decim_0.taps, 33554432); + decim_1.configure(taps_11k0_decim_1.taps, 131072); +} + +void ACARSProcessor::execute(const buffer_c8_t& buffer) { + /* 2.4576MHz, 2048 samples */ + + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); + const auto decimator_out = decim_1_out; + + /* 38.4kHz, 32 samples */ + feed_channel_stats(decimator_out); + + for(size_t i=0; i= 0.0f) ? 1 : 0; + const auto decoded_symbol = nrzi_decode(sliced_symbol); + + packet_builder.execute(decoded_symbol); +} + +void ACARSProcessor::payload_handler( + const baseband::Packet& packet +) { + const ACARSPacketMessage message { packet }; + shared_memory.application_queue.push(message); +} + +int main() { + EventDispatcher event_dispatcher { std::make_unique() }; + event_dispatcher.run(); + return 0; +} diff --git a/firmware/baseband/proc_acars.hpp b/firmware/baseband/proc_acars.hpp new file mode 100644 index 00000000..eeb8befd --- /dev/null +++ b/firmware/baseband/proc_acars.hpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek + * + * 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_ACARS_H__ +#define __PROC_ACARS_H__ + +#include "baseband_processor.hpp" +#include "baseband_thread.hpp" +#include "rssi_thread.hpp" + +#include "dsp_decimate.hpp" +#include "dsp_demodulate.hpp" +//#include "audio_compressor.hpp" + +#include "spectrum_collector.hpp" + +#include + +#include "baseband_processor.hpp" +#include "baseband_thread.hpp" +#include "rssi_thread.hpp" + +#include "channel_decimator.hpp" +#include "matched_filter.hpp" + +#include "clock_recovery.hpp" +#include "symbol_coding.hpp" +#include "packet_builder.hpp" +#include "baseband_packet.hpp" + +#include "message.hpp" + +#include +#include +#include + +#include "ais_baseband.hpp" + +class ACARSProcessor : public BasebandProcessor { +public: + ACARSProcessor(); + + void execute(const buffer_c8_t& buffer) override; + +private: + static constexpr size_t baseband_fs = 2457600; + + BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; + RSSIThread rssi_thread { NORMALPRIO + 10 }; + + std::array dst { }; + const buffer_c16_t dst_buffer { + dst.data(), + dst.size() + }; + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; + dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; + dsp::matched_filter::MatchedFilter mf { baseband::ais::square_taps_38k4_1t_p, 2 }; + + clock_recovery::ClockRecovery clock_recovery { + 19200, 2400, { 0.0555f }, + [this](const float symbol) { this->consume_symbol(symbol); } + }; + symbol_coding::NRZIDecoder nrzi_decode { }; + PacketBuilder packet_builder { + { 0b1001011010010110, 16, 1 }, // SYN, SYN + { }, + { 0b11111111, 8, 1 }, + [this](const baseband::Packet& packet) { + this->payload_handler(packet); + } + }; + + void consume_symbol(const float symbol); + void payload_handler(const baseband::Packet& packet); +}; + +#endif/*__PROC_ACARS_H__*/ diff --git a/firmware/common/acars_packet.cpp b/firmware/common/acars_packet.cpp new file mode 100644 index 00000000..fb959502 --- /dev/null +++ b/firmware/common/acars_packet.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek + * + * 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 "acars_packet.hpp" + +#include "crc.hpp" + +#include + +namespace acars { + +size_t Packet::length() const { + return packet_.size(); +} + +bool Packet::is_valid() const { + return length_valid() && crc_ok(); +} + +Timestamp Packet::received_at() const { + return packet_.timestamp(); +} + +uint8_t Packet::block_id() const { + return field_.read(96, 8); +} + +std::string Packet::registration_number() const { + std::string result; + result.reserve(7); + + const size_t character_length = 8; + for(size_t i=16; i<(16+7*character_length); i+=character_length) { + result += (field_.read(i, character_length) & 0x7F); + } + + return result; +} + +uint32_t Packet::read(const size_t start_bit, const size_t length) const { + return field_.read(start_bit, length); +} + +/*std::string Packet::text( + const size_t start_bit, + const size_t character_count +) const { + std::string result; + result.reserve(character_count); + + const size_t character_length = 6; + const size_t end_bit = start_bit + character_count * character_length; + for(size_t i=start_bit; i acars_fcs { 0x1021, 0x0000, 0x0000 }; + + for(size_t i=0; i +#include +#include + +namespace acars { + +class Packet { +public: + constexpr Packet( + const baseband::Packet& packet + ) : packet_ { packet }, + field_ { packet_ } + { + } + + size_t length() const; + + bool is_valid() const; + + Timestamp received_at() const; + + uint8_t block_id() const; + std::string registration_number() const; + + uint32_t read(const size_t start_bit, const size_t length) const; + //std::string text(const size_t start_bit, const size_t character_count) const; + + bool crc_ok() const; + +private: + using Reader = FieldReader; + using CRCReader = FieldReader; + + const baseband::Packet packet_; + const Reader field_; + + const size_t fcs_length = 16; + + size_t data_and_fcs_length() const; + size_t data_length() const; + + bool length_valid() const; +}; + +} /* namespace acars */ + +#endif/*__ACARS_PACKET_H__*/ diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 3f2a1752..c1f4e506 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -30,13 +30,14 @@ #include #include -#include "pocsag_packet.hpp" #include "baseband_packet.hpp" + +#include "acars_packet.hpp" +#include "adsb_frame.hpp" #include "ert_packet.hpp" -#include "tpms_packet.hpp" #include "pocsag_packet.hpp" #include "sonde_packet.hpp" -#include "adsb_frame.hpp" +#include "tpms_packet.hpp" #include "jammer.hpp" #include "dsp_fir_taps.hpp" #include "dsp_iir.hpp" @@ -57,9 +58,10 @@ public: ChannelStatistics = 2, DisplayFrameSync = 3, AudioStatistics = 4, + Shutdown = 5, TPMSPacket = 6, - Shutdown = 8, - AISPacket = 7, + ACARSPacket = 7, + AISPacket = 8, ERTPacket = 9, SondePacket = 10, UpdateSpectrum = 11, @@ -357,6 +359,18 @@ public: pocsag::POCSAGPacket packet; }; +class ACARSPacketMessage : public Message { +public: + constexpr ACARSPacketMessage( + const baseband::Packet& packet + ) : Message { ID::ACARSPacket }, + packet { packet } + { + } + + baseband::Packet packet; +}; + class ADSBFrameMessage : public Message { public: constexpr ADSBFrameMessage( diff --git a/firmware/common/spi_image.hpp b/firmware/common/spi_image.hpp index 51ea0679..fe80c423 100644 --- a/firmware/common/spi_image.hpp +++ b/firmware/common/spi_image.hpp @@ -63,6 +63,7 @@ private: char c[4]; }; +constexpr image_tag_t image_tag_acars { 'P', 'A', 'C', 'A' }; constexpr image_tag_t image_tag_adsb_rx { 'P', 'A', 'D', 'R' }; constexpr image_tag_t image_tag_afsk_rx { 'P', 'A', 'F', 'R' }; constexpr image_tag_t image_tag_ais { 'P', 'A', 'I', 'S' }; diff --git a/sdcard/FREQMAN/ACARS.TXT b/sdcard/FREQMAN/ACARS.TXT new file mode 100644 index 00000000..2880dfb1 --- /dev/null +++ b/sdcard/FREQMAN/ACARS.TXT @@ -0,0 +1,17 @@ +f=131550000,d=World Primary +f=130425000,d=USA Additional +f=131125000,d=USA Additional +f=136700000,d=USA Additional +f=136750000,d=USA Additional +f=136800000,d=USA Additional +f=130025000,d=USA/CAN Secondary +f=129125000,d=USA/CAN Additional +f=130450000,d=USA/CAN Additional +f=131725000,d=Euro Primary +f=131525000,d=Euro Secondary +f=136900000,d=Euro Secondary +f=131850000,d=New European +f=136750000,d=New European +f=131450000,d=Japan Primary +f=136850000,d=SITA North America +f=131475000,d=Air Canada