diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 876caee76135b63d3ca8d126d667187fa0283508..254ed8196d82118f6d07fe7079c64561765d0850 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 0000000000000000000000000000000000000000..3dc54bf969be10161ed00fc08b6331a4e03b5f91 --- /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<FrequencyKeypadView>(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<ACARSLogger>(); + 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 0000000000000000000000000000000000000000..d6e5f2e71b3a86ebf0f5a7bef6e5ff13a874c195 --- /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<File::Error> 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<ACARSLogger> 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<const ACARSPacketMessage*>(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 6808e63175c4b12376baa375c9dfd04e76b7a668..f0385d2ddde2a505c3021efde3ca6cef753dcd07 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 1b6014c80c91097e35f5b4caba1ca2543bd98f0d..9587d3387fa9ba26f4aa13bbeba7e48f948a9e0d 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 7dd05d2e0d8aa6e0ff17fa54c1e5387086d6b19d..ef565cdbc0096eed540a5197281c0acc5d1753f6 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<std::string, int32_t>; using options_t = std::vector<option_t>; @@ -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 fbf146604ee633c6a00b19d20969e22735c9f70f..73b980aa4f060375f44ffb563b69a443ab6f912c 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 6319298b1dbe1ddca16217aa33db0a5a433de046..cac18c04f49067c584e5cc9e24a688f630a9b949 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 0f4a0de548d203a94907ba96b9e6cc287eccf062..cd4841d60e0410e6f48f4b645947c96e1f0ac583 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 "analog_audio_app.hpp" +#include "capture_app.hpp" #include "ert_app.hpp" -#include "tpms_app.hpp" #include "pocsag_app.hpp" -#include "capture_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<ADSBRxView>(); }, }, + { "ACARS: Planes", ui::Color::yellow(),&bitmap_icon_adsb, [&nav](){ nav.replace<ACARSAppView>(); }, }, { "AIS: Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.replace<AISAppView>(); } }, { "AFSK", ui::Color::yellow(),&bitmap_icon_receivers, [&nav](){ nav.replace<AFSKRxView>(); } }, - { "APRS", ui::Color::grey(), &bitmap_icon_aprs, [&nav](){ nav.replace<NotImplementedView>(); } }, { "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.replace<AnalogAudioView>(false); } }, - { "DMR framing", ui::Color::grey(), &bitmap_icon_dmr, [&nav](){ nav.replace<NotImplementedView>(); } }, { "ERT: Utility Meters", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.replace<ERTAppView>(); } }, { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.replace<POCSAGAppView>(); } }, + { "Radiosondes", ui::Color::yellow(),&bitmap_icon_sonde, [&nav](){ nav.replace<SondeView>(); } }, + { "TPMS: Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.replace<TPMSAppView>(); } }, + { "APRS", ui::Color::grey(), &bitmap_icon_aprs, [&nav](){ nav.replace<NotImplementedView>(); } }, + { "DMR framing", ui::Color::grey(), &bitmap_icon_dmr, [&nav](){ nav.replace<NotImplementedView>(); } }, { "SIGFOX", ui::Color::grey(), &bitmap_icon_fox, [&nav](){ nav.replace<NotImplementedView>(); } }, // SIGFRXView { "LoRa", ui::Color::grey(), &bitmap_icon_lora, [&nav](){ nav.replace<NotImplementedView>(); } }, - { "Radiosondes", ui::Color::yellow(),&bitmap_icon_sonde, [&nav](){ nav.replace<SondeView>(); } }, { "SSTV", ui::Color::grey(), &bitmap_icon_sstv, [&nav](){ nav.replace<NotImplementedView>(); } }, { "TETRA framing", ui::Color::grey(), &bitmap_icon_tetra, [&nav](){ nav.replace<NotImplementedView>(); } }, - { "TPMS: Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.replace<TPMSAppView>(); } }, }); 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<ADSBTxView>(); } }, { "APRS", ui::Color::orange(), &bitmap_icon_aprs, [&nav](){ nav.push<APRSTXView>(); } }, { "BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav](){ nav.push<BHTView>(); } }, - { "Custom remote", ui::Color::grey(), &bitmap_icon_remote, [&nav](){ nav.push<RemoteView>(); } }, { "Jammer", ui::Color::yellow(), &bitmap_icon_jammer, [&nav](){ nav.push<JammerView>(); } }, { "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push<KeyfobView>(); } }, { "Microphone", ui::Color::green(), &bitmap_icon_microphone, [&nav](){ nav.push<MicTXView>(); } }, @@ -369,7 +370,8 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { { "Soundboard", ui::Color::green(), &bitmap_icon_soundboard, [&nav](){ nav.push<SoundBoardView>(); } }, { "SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav](){ nav.push<SSTVTXView>(); } }, { "TEDI/LCR AFSK", ui::Color::yellow(), &bitmap_icon_lcr, [&nav](){ nav.push<LCRView>(); } }, - { "TouchTunes remote", ui::Color::yellow(), nullptr, [&nav](){ nav.push<TouchTunesView>(); } }, + { "TouchTunes remote", ui::Color::yellow(), &bitmap_icon_remote [&nav](){ nav.push<TouchTunesView>(); } }, + { "Custom remote", ui::Color::grey(), &bitmap_icon_remote, [&nav](){ nav.push<RemoteView>(); } }, }); on_left = [&nav](){ nav.pop(); }; } diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index 13f0a62fefed00bbc6333d02cd589ac9fb119842..94311c06565b94890f6d31ed5935da75d64b25c2 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 0000000000000000000000000000000000000000..e0077f8e1c11bd994563d3bc1da757ed6f6a8588 --- /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<decimator_out.count; i++) { + if( mf.execute_once(decimator_out.p[i]) ) { + clock_recovery(mf.get_output()); + } + } +} + +void ACARSProcessor::consume_symbol( + const float raw_symbol +) { + const uint_fast8_t sliced_symbol = (raw_symbol >= 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<ACARSProcessor>() }; + event_dispatcher.run(); + return 0; +} diff --git a/firmware/baseband/proc_acars.hpp b/firmware/baseband/proc_acars.hpp new file mode 100644 index 0000000000000000000000000000000000000000..eeb8befdbd9144955eca17c1023ccde1d2aa0468 --- /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 <cstdint> + +#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 <cstdint> +#include <cstddef> +#include <bitset> + +#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<complex16_t, 512> 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::FixedErrorFilter> clock_recovery { + 19200, 2400, { 0.0555f }, + [this](const float symbol) { this->consume_symbol(symbol); } + }; + symbol_coding::NRZIDecoder nrzi_decode { }; + PacketBuilder<BitPattern, NeverMatch, BitPattern> 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 0000000000000000000000000000000000000000..fb9595020713d28d740d81620944d010d4332b99 --- /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 <cstdlib> + +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<end_bit; i+=character_length) { + result += char_to_ascii(field_.read(i, character_length)); + } + + return result; +}*/ + +bool Packet::crc_ok() const { + CRCReader field_crc { packet_ }; + CRC<16> acars_fcs { 0x1021, 0x0000, 0x0000 }; + + for(size_t i=0; i<data_length(); i+=8) { + acars_fcs.process_byte(field_crc.read(i, 8)); + } + + return (acars_fcs.checksum() == field_crc.read(data_length(), fcs_length)); +} + +size_t Packet::data_and_fcs_length() const { + return length() - 8; +} + +size_t Packet::data_length() const { + return data_and_fcs_length() - fcs_length; +} + +bool Packet::length_valid() const { + const size_t extra_bits = data_and_fcs_length() & 7; + if( extra_bits != 0 ) { + return false; + } + + return true; +} + +} /* namespace ais */ diff --git a/firmware/common/acars_packet.hpp b/firmware/common/acars_packet.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c8081b30311b43ca027ec665c33abe07678ccb7c --- /dev/null +++ b/firmware/common/acars_packet.hpp @@ -0,0 +1,75 @@ +/* + * 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 __ACARS_PACKET_H__ +#define __ACARS_PACKET_H__ + +#include "baseband_packet.hpp" +#include "field_reader.hpp" + +#include <cstdint> +#include <cstddef> +#include <string> + +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<baseband::Packet, BitRemapByteReverse>; + using CRCReader = FieldReader<baseband::Packet, BitRemapNone>; + + 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 3f2a1752f8ac30f1263b228dfbfa42f91afafbfe..c1f4e506ea3b471547cfacb5309baef6f9258cf0 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -30,13 +30,14 @@ #include <functional> #include <algorithm> -#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 51ea06791bf81b67cb48ff338e3f722f1636688c..fe80c42310d48a83cb9df5a0afc15fa0b5170126 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 0000000000000000000000000000000000000000..2880dfb1c472beb9fac7b8e905d22e6d4e37a79d --- /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