mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-10-01 01:26:06 -04:00
ADS-B TX works well enough for dump1090 and gr-air-modes
Hooked ADS-B RX to baseband instead of debug IQ file, not tested
This commit is contained in:
parent
b57b41753f
commit
5a67a7080a
@ -139,7 +139,6 @@ set(CPPSRC
|
||||
debounce.cpp
|
||||
touch.cpp
|
||||
touch_adc.cpp
|
||||
protocols/adsb.cpp
|
||||
protocols/aprs.cpp
|
||||
protocols/ax25.cpp
|
||||
protocols/bht.cpp
|
||||
@ -217,6 +216,8 @@ set(CPPSRC
|
||||
${COMMON}/pocsag_packet.cpp
|
||||
${COMMON}/pocsag.cpp
|
||||
${COMMON}/morse.cpp
|
||||
${COMMON}/adsb_frame.cpp
|
||||
${COMMON}/adsb.cpp
|
||||
ais_app.cpp
|
||||
tpms_app.cpp
|
||||
pocsag_app.cpp
|
||||
|
@ -24,7 +24,6 @@
|
||||
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
|
||||
|
||||
//TODO: De bruijn sequence scanner for encoders
|
||||
//TODO: Waveform viewer
|
||||
//TEST: Mic tx
|
||||
//TEST: Menuview refresh, seems to blink a lot
|
||||
//TEST: Check AFSK transmit end, skips last bits ?
|
||||
|
@ -1,198 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 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 "adsb.hpp"
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
namespace adsb {
|
||||
|
||||
uint8_t adsb_frame::get_DF() {
|
||||
return (raw_data[0] >> 3);
|
||||
}
|
||||
|
||||
uint8_t adsb_frame::get_msg_type() {
|
||||
return (raw_data[4] >> 3);
|
||||
}
|
||||
|
||||
uint32_t adsb_frame::get_ICAO_address() {
|
||||
return (raw_data[1] << 16) + (raw_data[2] << 8) + raw_data[3];
|
||||
}
|
||||
|
||||
void adsb_frame::clear() {
|
||||
index = 0;
|
||||
memset(raw_data, 0, 14);
|
||||
}
|
||||
|
||||
void adsb_frame::push_byte(uint8_t byte) {
|
||||
if (index >= 14)
|
||||
return;
|
||||
|
||||
raw_data[index++] = byte;
|
||||
}
|
||||
|
||||
uint8_t adsb_frame::get_byte(uint8_t index) {
|
||||
if (index >= 14)
|
||||
return 0;
|
||||
|
||||
return raw_data[index];
|
||||
}
|
||||
|
||||
std::string adsb_frame::get_callsign() {
|
||||
uint64_t callsign_coded = 0;
|
||||
uint32_t c;
|
||||
std::string callsign = "";
|
||||
|
||||
// Frame bytes to long
|
||||
for (c = 5; c < 11; c++) {
|
||||
callsign_coded <<= 8;
|
||||
callsign_coded |= raw_data[c];
|
||||
}
|
||||
|
||||
// Long to 6-bit characters
|
||||
for (c = 0; c < 8; c++) {
|
||||
callsign.append(1, icao_id_lut[(callsign_coded >> 42) & 0x3F]);
|
||||
callsign_coded <<= 6;
|
||||
}
|
||||
|
||||
return callsign;
|
||||
}
|
||||
|
||||
void adsb_frame::make_CRC() {
|
||||
uint8_t adsb_crc[14]; // Temp buffer
|
||||
uint8_t b, c, s, bitn;
|
||||
const uint32_t crc_poly = 0x1205FFF;
|
||||
|
||||
// Clear CRC
|
||||
raw_data[11] = 0x00;
|
||||
raw_data[12] = 0x00;
|
||||
raw_data[13] = 0x00;
|
||||
|
||||
// Compute CRC
|
||||
memcpy(adsb_crc, raw_data, 14);
|
||||
for (c = 0; c < 11; c++) {
|
||||
for (b = 0; b < 8; b++) {
|
||||
if ((adsb_crc[c] << b) & 0x80) {
|
||||
for (s = 0; s < 25; s++) {
|
||||
bitn = (c * 8) + b + s;
|
||||
if ((crc_poly >> s) & 1) adsb_crc[bitn >> 3] ^= (0x80 >> (bitn & 7));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert CRC in frame
|
||||
memcpy(&raw_data[11], &adsb_crc[11], 3);
|
||||
}
|
||||
|
||||
void make_frame_mode_s(adsb_frame& frame, const uint32_t ICAO_address) {
|
||||
frame.clear();
|
||||
frame.push_byte((17 << 3) | 5); // DF17 and CA
|
||||
frame.push_byte(ICAO_address >> 16);
|
||||
frame.push_byte(ICAO_address >> 8);
|
||||
frame.push_byte(ICAO_address & 0xFF);
|
||||
}
|
||||
|
||||
void generate_frame_id(adsb_frame& frame, const uint32_t ICAO_address, std::string & callsign) {
|
||||
std::string callsign_formatted(8, '_');
|
||||
uint64_t callsign_coded = 0;
|
||||
uint32_t c, s;
|
||||
char ch;
|
||||
|
||||
make_frame_mode_s(frame, ICAO_address);
|
||||
|
||||
frame.push_byte(4 << 3); // TC = 4: Aircraft ID
|
||||
|
||||
// Translate and encode callsign
|
||||
for (c = 0; c < 8; c++) {
|
||||
ch = callsign[c];
|
||||
|
||||
for (s = 0; s < 64; s++)
|
||||
if (ch == icao_id_lut[s]) break;
|
||||
|
||||
if (s >= 64) {
|
||||
ch = ' '; // Invalid character
|
||||
s = 32;
|
||||
}
|
||||
callsign_coded |= ((uint64_t)s << ((7 - c) * 6));
|
||||
callsign[c] = ch;
|
||||
}
|
||||
|
||||
// Insert callsign in frame
|
||||
for (c = 0; c < 6; c++)
|
||||
frame.push_byte((callsign_coded >> ((5 - c) * 8)) & 0xFF);
|
||||
|
||||
frame.make_CRC();
|
||||
}
|
||||
|
||||
void generate_frame_emergency(adsb_frame& frame, const uint32_t ICAO_address, const uint8_t code) {
|
||||
make_frame_mode_s(frame, ICAO_address);
|
||||
|
||||
frame.push_byte((28 << 3) + 1); // TC = 28 (Emergency), subtype = 1 (Emergency)
|
||||
frame.push_byte(code << 5);
|
||||
|
||||
frame.make_CRC();
|
||||
}
|
||||
|
||||
/*
|
||||
void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint32_t altitude,
|
||||
const float latitude, const float longitude) {
|
||||
uint8_t c, time_parity;
|
||||
uint32_t altitude_coded;
|
||||
uint32_t LAT, LON;
|
||||
float delta_lat, yz, rlat, delta_lon, xz;
|
||||
|
||||
LAT = 0;
|
||||
|
||||
make_frame_mode_s(adsb_frame, ICAO_address);
|
||||
|
||||
adsb_frame[4] = 11 << 3; // TC = 11: Airborne position
|
||||
|
||||
altitude_coded = (altitude + 1000) / 25; // Can be coded in 100ft steps also
|
||||
|
||||
// LAT:
|
||||
// index j = floor(59*latcprE-60*latcprO+0.50)
|
||||
// latE = DlatE*(mod(j,60)+latcprE)
|
||||
// latO = DlatO*(mod(j,59)+latcprO)
|
||||
// if latE >= 270 -> latE -= 360
|
||||
// if latO >= 270 -> latO -= 360
|
||||
//time_parity = 0; // 0~1
|
||||
//delta_lat = 90.0 / (60.0 - (time_parity / 4.0));
|
||||
//yz = 524288.0 * (mod(lat, delta_lat) / delta_lat); // Round to int !
|
||||
//rlat = delta_lat * ((yz / 524288.0) + int(lat / delta_lat));
|
||||
//delta_lon = 360.0 / (NL(rlat) - time_parity);
|
||||
//xz = 524288.0 * (mod(lon, delta_lon) / delta_lon); // Round to int !
|
||||
if (time_parity) {
|
||||
A = sign(rlat0)[NL(rlat0) - NL(rlat1)];
|
||||
}
|
||||
// int xz and yz, then:
|
||||
// xz >>= 2;
|
||||
// yz >>= 2;
|
||||
// To get 17 bits
|
||||
|
||||
// aaaaaaa Q bbbb
|
||||
adsb_frame[5] = ((altitude_coded & 0x7F0) >> 3) | 1;
|
||||
adsb_frame[6] = ((altitude_coded & 0x00F) << 4) | (LAT >> 15); // Then 0, even/odd, and the 2 LAT-CPR MSBs
|
||||
}
|
||||
*/
|
||||
|
||||
} /* namespace adsb */
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 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 <cstring>
|
||||
#include <string>
|
||||
|
||||
#ifndef __ADSB_H__
|
||||
#define __ADSB_H__
|
||||
|
||||
#define ADSB_PREAMBLE_LENGTH 16
|
||||
|
||||
namespace adsb {
|
||||
|
||||
struct adsb_frame {
|
||||
public:
|
||||
uint8_t get_DF();
|
||||
uint8_t get_msg_type();
|
||||
uint32_t get_ICAO_address();
|
||||
std::string get_callsign();
|
||||
|
||||
void push_byte(uint8_t byte);
|
||||
uint8_t get_byte(uint8_t index); // DEBUG
|
||||
void make_CRC();
|
||||
void clear();
|
||||
|
||||
private:
|
||||
uint8_t index { 0 };
|
||||
uint8_t raw_data[14] { }; // 112 bits at most
|
||||
};
|
||||
|
||||
const char icao_id_lut[65] = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######";
|
||||
const uint8_t adsb_preamble[16] = { 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
void make_frame_mode_s(adsb_frame& frame, const uint32_t ICAO_address);
|
||||
void generate_frame_id(adsb_frame&, const uint32_t ICAO_address, std::string& callsign);
|
||||
//void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint32_t altitude,
|
||||
// const float latitude, const float longitude);
|
||||
void generate_frame_emergency(adsb_frame&, const uint32_t ICAO_address, const uint8_t code);
|
||||
|
||||
} /* namespace adsb */
|
||||
|
||||
#endif/*__ADSB_H__*/
|
@ -26,7 +26,6 @@
|
||||
|
||||
#include "adsb.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
@ -39,25 +38,35 @@ using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
template<>
|
||||
void RecentEntriesTable<ADSBRecentEntries>::draw(
|
||||
const Entry& entry,
|
||||
const Rect& target_rect,
|
||||
Painter& painter,
|
||||
const Style& style
|
||||
) {
|
||||
painter.draw_string(target_rect.location(), style, to_string_hex_array((uint8_t*)entry.raw_data, 10) + " " + entry.time);
|
||||
}
|
||||
|
||||
void ADSBRxView::focus() {
|
||||
offset_field.focus();
|
||||
offset_field.set_value(13179);
|
||||
}
|
||||
|
||||
ADSBRxView::~ADSBRxView() {
|
||||
//transmitter_model.disable();
|
||||
//baseband::shutdown();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
bool ADSBRxView::analyze(uint64_t offset) {
|
||||
/*bool ADSBRxView::analyze(uint64_t offset) {
|
||||
Coord lcd_x = 0, lcd_y = 0;
|
||||
int8_t re, im;
|
||||
adsb_frame frame;
|
||||
int16_t file_data[128]; // 256 bytes / 2 IQ / 16 bits = 64 samples
|
||||
complex8_t iq_data[256]; // 256 samples
|
||||
uint64_t file_offset = 0;
|
||||
uint8_t data_put = 0, data_get = 0;
|
||||
int16_t f_re, f_im;
|
||||
int8_t re, im;
|
||||
uint32_t c;
|
||||
uint8_t level, bit, byte;
|
||||
Color mark_color;
|
||||
@ -224,42 +233,64 @@ bool ADSBRxView::analyze(uint64_t offset) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}*/
|
||||
|
||||
void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
|
||||
rtc::RTC datetime;
|
||||
std::string str_timestamp;
|
||||
auto frame = message->frame;
|
||||
auto& entry = ::on_packet(recent, frame.get_ICAO_address());
|
||||
|
||||
rtcGetTime(&RTCD1, &datetime);
|
||||
str_timestamp = to_string_dec_uint(datetime.hour(), 2, '0') + ":" +
|
||||
to_string_dec_uint(datetime.minute(), 2, '0') + ":" +
|
||||
to_string_dec_uint(datetime.second(), 2, '0');
|
||||
entry.set_time(str_timestamp);
|
||||
entry.set_raw(frame.get_raw_data());
|
||||
recent_entries_view.set_dirty();
|
||||
}
|
||||
|
||||
ADSBRxView::ADSBRxView(NavigationView& nav) {
|
||||
|
||||
//baseband::run_image(portapack::spi_flash::image_tag_adsb_rx);
|
||||
baseband::run_image(portapack::spi_flash::image_tag_adsb_rx);
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
//&labels,
|
||||
&offset_field,
|
||||
&button_ffw,
|
||||
//&button_ffw,
|
||||
&text_debug_a,
|
||||
&text_debug_b,
|
||||
&text_debug_c,
|
||||
&text_debug_d,
|
||||
&text_debug_e
|
||||
&recent_entries_view
|
||||
});
|
||||
|
||||
// File must be 16bit complex @ 2Msps !
|
||||
recent_entries_view.set_parent_rect({ 0, 64, 240, 224 });
|
||||
|
||||
auto result = iq_file.open("ADSB.C16");
|
||||
// File must be 16bit complex @ 2Msps !
|
||||
/*auto result = iq_file.open("ADSB.C16");
|
||||
if (result.is_valid()) {
|
||||
text_debug_a.set("Can't open file");
|
||||
}
|
||||
}*/
|
||||
|
||||
offset_field.on_change = [this, &nav](int32_t value) {
|
||||
/*offset_field.on_change = [this, &nav](int32_t value) {
|
||||
// TODO
|
||||
};
|
||||
};*/
|
||||
|
||||
button_ffw.on_select = [this, &nav](Button&) {
|
||||
auto new_view = nav.push<GeoMapView>();
|
||||
/*while (!analyze(f_offset)) {
|
||||
/*button_ffw.on_select = [this, &nav](Button&) {
|
||||
//nav.push<GeoMapView>();
|
||||
while (!analyze(f_offset)) {
|
||||
f_offset++;
|
||||
}
|
||||
offset_field.set_value(f_offset);
|
||||
f_offset++;*/
|
||||
};
|
||||
f_offset++;
|
||||
};*/
|
||||
|
||||
baseband::set_adsb();
|
||||
|
||||
receiver_model.set_tuning_frequency(1090000000);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
|
||||
receiver_model.set_sampling_rate(2000000);
|
||||
receiver_model.set_baseband_bandwidth(2500000);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@ -26,12 +26,48 @@
|
||||
//#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
struct ADSBRecentEntry {
|
||||
using Key = uint32_t;
|
||||
|
||||
static constexpr Key invalid_key = 0xffffffff;
|
||||
|
||||
uint32_t ICAO_address;
|
||||
uint8_t raw_data[14] { }; // 112 bits at most
|
||||
std::string time { "" };
|
||||
|
||||
ADSBRecentEntry(
|
||||
) : ADSBRecentEntry { 0 }
|
||||
{
|
||||
}
|
||||
|
||||
ADSBRecentEntry(
|
||||
const uint32_t ICAO_address
|
||||
) : ICAO_address { ICAO_address }
|
||||
{
|
||||
}
|
||||
|
||||
Key key() const {
|
||||
return ICAO_address;
|
||||
}
|
||||
|
||||
void set_time(std::string& new_time) {
|
||||
time = new_time;
|
||||
}
|
||||
|
||||
void set_raw(uint8_t * raw_ptr) {
|
||||
memcpy(raw_data, raw_ptr, 14);
|
||||
}
|
||||
};
|
||||
|
||||
using ADSBRecentEntries = RecentEntries<ADSBRecentEntry>;
|
||||
|
||||
class ADSBRxView : public View {
|
||||
public:
|
||||
ADSBRxView(NavigationView& nav);
|
||||
@ -42,16 +78,24 @@ public:
|
||||
std::string title() const override { return "ADS-B debug"; };
|
||||
|
||||
private:
|
||||
static constexpr float k = 1.0f / 128.0f;
|
||||
//static constexpr float k = 1.0f / 128.0f;
|
||||
|
||||
File iq_file { };
|
||||
size_t f_offset { 0 };
|
||||
//File iq_file { };
|
||||
//size_t f_offset { 0 };
|
||||
|
||||
bool analyze(uint64_t offset);
|
||||
//bool analyze(uint64_t offset);
|
||||
void on_frame(const ADSBFrameMessage * message);
|
||||
|
||||
Labels labels {
|
||||
const RecentEntriesColumns columns { {
|
||||
{ "Raw", 21 },
|
||||
{ "Time", 8 },
|
||||
} };
|
||||
ADSBRecentEntries recent { };
|
||||
RecentEntriesView<RecentEntries<ADSBRecentEntry>> recent_entries_view { columns, recent };
|
||||
|
||||
/*Labels labels {
|
||||
{ { 0 * 8, 0 * 8 }, "Test", Color::light_grey() }
|
||||
};
|
||||
};*/
|
||||
|
||||
NumberField offset_field {
|
||||
{ 0, 0 },
|
||||
@ -60,10 +104,6 @@ private:
|
||||
1,
|
||||
'0'
|
||||
};
|
||||
Text text_debug_e {
|
||||
{ 7 * 8, 0 * 16, 14 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_debug_a {
|
||||
{ 0 * 8, 1 * 16, 30 * 8, 16 },
|
||||
@ -77,24 +117,19 @@ private:
|
||||
{ 0 * 8, 3 * 16, 30 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
Text text_debug_d {
|
||||
{ 0 * 8, 4 * 16, 30 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Button button_ffw {
|
||||
/*Button button_ffw {
|
||||
{ 184, 0 * 16, 56, 16 },
|
||||
"FFW"
|
||||
};
|
||||
|
||||
/*
|
||||
MessageHandlerRegistration message_handler_tx_done {
|
||||
Message::ID::TXDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
|
||||
this->on_txdone(message.done);
|
||||
}
|
||||
};*/
|
||||
|
||||
MessageHandlerRegistration message_handler_frame {
|
||||
Message::ID::ADSBFrame,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const ADSBFrameMessage*>(p);
|
||||
this->on_frame(message);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "string_format.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
@ -37,7 +36,6 @@ using namespace portapack;
|
||||
namespace ui {
|
||||
|
||||
void ADSBTxView::focus() {
|
||||
//sym_icao.focus();
|
||||
tx_view.focus();
|
||||
}
|
||||
|
||||
@ -46,28 +44,38 @@ ADSBTxView::~ADSBTxView() {
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void ADSBTxView::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
void ADSBTxView::paint(Painter&) {
|
||||
button_callsign.set_text(callsign);
|
||||
}
|
||||
|
||||
void ADSBTxView::generate_frame() {
|
||||
uint32_t c;
|
||||
std::string str_debug;
|
||||
uint8_t * bin_ptr = shared_memory.bb_data.data;
|
||||
|
||||
generate_frame_id(frame, sym_icao.value_hex_u64(), callsign);
|
||||
generate_frame_id(frames[0], sym_icao.value_hex_u64(), callsign);
|
||||
generate_frame_pos(frames[1], sym_icao.value_hex_u64(), 5000, field_lat_degrees.value(), field_lon_degrees.value(), 0);
|
||||
generate_frame_pos(frames[2], sym_icao.value_hex_u64(), 5000, field_lat_degrees.value(), field_lon_degrees.value(), 1);
|
||||
|
||||
memset(adsb_bin, 0, 112);
|
||||
memset(bin_ptr, 0, 240);
|
||||
|
||||
auto raw_ptr = frames[0].get_raw_data();
|
||||
|
||||
memcpy(bin_ptr, adsb_preamble, 16);
|
||||
|
||||
// Convert to binary (1 byte per bit, faster for baseband code)
|
||||
for (c = 0; c < 112; c++) {
|
||||
if ((frame.get_byte(c >> 3) << (c & 7)) & 0x80)
|
||||
adsb_bin[c] = 1;
|
||||
if ((raw_ptr[c >> 3] << (c & 7)) & 0x80) {
|
||||
bin_ptr[(c * 2) + 16] = 1;
|
||||
bin_ptr[(c * 2) + 16 + 1] = 0;
|
||||
} else {
|
||||
bin_ptr[(c * 2) + 16] = 0;
|
||||
bin_ptr[(c * 2) + 16 + 1] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Display in hex for debug
|
||||
//text_frame_a.set(to_string_hex_array(frame.get_byte(0), 7));
|
||||
//text_frame_b.set(to_string_hex_array(frame.get_byte(7), 7));
|
||||
text_frame_a.set(to_string_hex_array(frames[0].get_raw_data(), 7));
|
||||
text_frame_b.set(to_string_hex_array(frames[0].get_raw_data() + 7, 7));
|
||||
|
||||
button_callsign.set_text(callsign);
|
||||
}
|
||||
@ -75,28 +83,78 @@ void ADSBTxView::generate_frame() {
|
||||
bool ADSBTxView::start_tx() {
|
||||
generate_frame();
|
||||
|
||||
memcpy(shared_memory.bb_data.data, adsb_bin, 112);
|
||||
baseband::set_adsb();
|
||||
|
||||
transmitter_model.set_tuning_frequency(434000000); // DEBUG
|
||||
transmitter_model.set_tuning_frequency(434000000);
|
||||
transmitter_model.set_sampling_rate(4000000U);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_vga(40);
|
||||
transmitter_model.set_baseband_bandwidth(2500000);
|
||||
transmitter_model.set_baseband_bandwidth(10000000);
|
||||
transmitter_model.enable();
|
||||
|
||||
return false; // DEBUG
|
||||
baseband::set_adsb();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ADSBTxView::on_txdone(const bool v) {
|
||||
//size_t sr;
|
||||
|
||||
if (v) {
|
||||
transmitter_model.disable();
|
||||
tx_view.set_transmitting(false);
|
||||
//progress.set_value(0);
|
||||
} else {
|
||||
//progress.set_value(n);
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBTxView::rotate_frames() {
|
||||
// DEBUG
|
||||
uint8_t * bin_ptr = shared_memory.bb_data.data;
|
||||
uint8_t * raw_ptr;
|
||||
uint32_t frame_index = 0, plane_index = 0;
|
||||
uint32_t c, regen = 0;
|
||||
float offs = 0;
|
||||
|
||||
for (;;) {
|
||||
if (!regen) {
|
||||
regen = 10;
|
||||
|
||||
generate_frame_id(frames[0], plane_index, "DEMO" + to_string_dec_uint(plane_index));
|
||||
generate_frame_pos(frames[1], plane_index, 5000, plane_lats[plane_index]/8 + offs + 38.5, plane_lons[plane_index]/8 + 125.8, 0);
|
||||
generate_frame_pos(frames[2], plane_index, 5000, plane_lats[plane_index]/8 + offs + 38.5, plane_lons[plane_index]/8 + 125.8, 1);
|
||||
generate_frame_identity(frames[3], plane_index, 1337);
|
||||
|
||||
if (plane_index == 11)
|
||||
plane_index = 0;
|
||||
else
|
||||
plane_index++;
|
||||
|
||||
offs += 0.001;
|
||||
}
|
||||
|
||||
memset(bin_ptr, 0, 240);
|
||||
|
||||
raw_ptr = frames[frame_index].get_raw_data();
|
||||
|
||||
memcpy(bin_ptr, adsb_preamble, 16);
|
||||
|
||||
// Convert to binary (1 byte per bit, faster for baseband code)
|
||||
for (c = 0; c < 112; c++) {
|
||||
if ((raw_ptr[c >> 3] << (c & 7)) & 0x80) {
|
||||
bin_ptr[(c * 2) + 16] = 1;
|
||||
bin_ptr[(c * 2) + 16 + 1] = 0;
|
||||
} else {
|
||||
bin_ptr[(c * 2) + 16] = 0;
|
||||
bin_ptr[(c * 2) + 16 + 1] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
baseband::set_adsb();
|
||||
|
||||
chThdSleepMilliseconds(5);
|
||||
|
||||
if (frame_index == 3) {
|
||||
frame_index = 0;
|
||||
if (regen)
|
||||
regen--;
|
||||
} else {
|
||||
frame_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,16 +184,14 @@ ADSBTxView::ADSBTxView(NavigationView& nav) {
|
||||
|
||||
options_format.set_by_value(17); // Mode S
|
||||
|
||||
options_format.on_change = [this](size_t i, int32_t v) {
|
||||
(void)i;
|
||||
(void)v;
|
||||
options_format.on_change = [this](size_t, int32_t) {
|
||||
generate_frame();
|
||||
};
|
||||
sym_icao.on_change = [this]() {
|
||||
generate_frame();
|
||||
};
|
||||
button_callsign.on_select = [this, &nav](Button&) {
|
||||
text_prompt(nav, &callsign, 9);
|
||||
text_prompt(nav, &callsign, 8);
|
||||
};
|
||||
|
||||
field_altitude.set_value(11000);
|
||||
@ -146,19 +202,24 @@ ADSBTxView::ADSBTxView(NavigationView& nav) {
|
||||
field_lon_minutes.set_value(0);
|
||||
field_lon_seconds.set_value(0);
|
||||
|
||||
field_lat_degrees.on_change = [this](int32_t) {
|
||||
generate_frame();
|
||||
};
|
||||
field_lon_degrees.on_change = [this](int32_t) {
|
||||
generate_frame();
|
||||
};
|
||||
|
||||
for (c = 0; c < 4; c++)
|
||||
field_squawk.set_sym(c, 0);
|
||||
|
||||
generate_frame();
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
// Shouldn't be able to edit frequency
|
||||
return;
|
||||
};
|
||||
receiver_model.set_tuning_frequency(434000000); // DEBUG
|
||||
|
||||
tx_view.on_start = [this]() {
|
||||
if (start_tx())
|
||||
tx_view.set_transmitting(true);
|
||||
//rotate_frames();
|
||||
};
|
||||
|
||||
tx_view.on_stop = [this]() {
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
ADSBTxView(NavigationView& nav);
|
||||
~ADSBTxView();
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void paint(Painter&) override;
|
||||
|
||||
void focus() override;
|
||||
|
||||
@ -54,16 +54,44 @@ private:
|
||||
SEQUENCE
|
||||
};
|
||||
|
||||
const float plane_lats[12] = {
|
||||
0,
|
||||
-1,
|
||||
-2,
|
||||
-3,
|
||||
-4,
|
||||
-5,
|
||||
-4.5,
|
||||
-5,
|
||||
-4,
|
||||
-3,
|
||||
-2,
|
||||
-1
|
||||
};
|
||||
const float plane_lons[12] = {
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
0,
|
||||
-1,
|
||||
-2,
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
};
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
std::string callsign = "KLM1023 ";
|
||||
std::string callsign = "PORTAPAC";
|
||||
|
||||
adsb_frame frame { };
|
||||
uint8_t adsb_bin[112]; // 112 bit data block
|
||||
ADSBFrame frames[4] { };
|
||||
|
||||
bool start_tx();
|
||||
void generate_frame();
|
||||
void generate_frame_pos();
|
||||
void rotate_frames();
|
||||
void on_txdone(const bool v);
|
||||
|
||||
const Style style_val {
|
||||
@ -82,8 +110,8 @@ private:
|
||||
{ { 2 * 8, 4 * 8 }, "ICAO24:", Color::light_grey() },
|
||||
{ { 2 * 8, 7 * 8 }, "ID:", Color::light_grey() },
|
||||
{ { 2 * 8, 10 * 8 }, "Altitude: feet", Color::light_grey() },
|
||||
{ { 2 * 8, 12 * 8 }, "Latitude: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font
|
||||
{ { 2 * 8, 14 * 8 }, "Longitude: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font
|
||||
{ { 2 * 8, 12 * 8 }, "Latitude: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font
|
||||
{ { 2 * 8, 14 * 8 }, "Longitude: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font
|
||||
{ { 15 * 8, 18 * 8 }, "Squawk", Color::light_grey() }
|
||||
};
|
||||
|
||||
@ -117,23 +145,23 @@ private:
|
||||
};
|
||||
|
||||
NumberField field_lat_degrees {
|
||||
{ 12 * 8, 6 * 16 }, 3, { -90, 90 }, 1, ' '
|
||||
{ 12 * 8, 6 * 16 }, 4, { -90, 90 }, 1, ' '
|
||||
};
|
||||
NumberField field_lat_minutes {
|
||||
{ 16 * 8, 6 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
{ 17 * 8, 6 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
NumberField field_lat_seconds {
|
||||
{ 19 * 8, 6 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
{ 20 * 8, 6 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
|
||||
NumberField field_lon_degrees {
|
||||
{ 12 * 8, 7 * 16 }, 3, { -90, 90 }, 1, ' '
|
||||
{ 12 * 8, 7 * 16 }, 4, { -180, 180 }, 1, ' '
|
||||
};
|
||||
NumberField field_lon_minutes {
|
||||
{ 16 * 8, 7 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
{ 17 * 8, 7 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
NumberField field_lon_seconds {
|
||||
{ 19 * 8, 7 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
{ 20 * 8, 7 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
|
||||
Checkbox check_emergency {
|
||||
@ -160,8 +188,8 @@ private:
|
||||
|
||||
TransmitterView tx_view {
|
||||
16 * 16,
|
||||
1090000000,
|
||||
2000000,
|
||||
0,
|
||||
0,
|
||||
true
|
||||
};
|
||||
|
||||
|
@ -76,7 +76,7 @@ void TransmitterView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
}
|
||||
|
||||
void TransmitterView::on_bandwidth_changed(uint32_t bandwidth) {
|
||||
transmitter_model.set_bandwidth(bandwidth);
|
||||
//transmitter_model.set_bandwidth(bandwidth);
|
||||
}
|
||||
|
||||
void TransmitterView::set_transmitting(const bool transmitting) {
|
||||
@ -92,7 +92,7 @@ void TransmitterView::set_transmitting(const bool transmitting) {
|
||||
}
|
||||
|
||||
void TransmitterView::on_show() {
|
||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
//field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
}
|
||||
|
||||
void TransmitterView::focus() {
|
||||
|
@ -275,12 +275,19 @@ macro(DeclareTargets chunk_tag name)
|
||||
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img)
|
||||
endmacro()
|
||||
|
||||
### ADS-B RX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_adsbrx.cpp
|
||||
)
|
||||
DeclareTargets(PADR adsbrx)
|
||||
|
||||
### ADS-B TX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_adsbtx.cpp
|
||||
)
|
||||
DeclareTargets(PADS ads)
|
||||
DeclareTargets(PADT adsbtx)
|
||||
|
||||
### AFSK
|
||||
|
||||
|
@ -35,7 +35,14 @@ class ChannelStatsCollector {
|
||||
public:
|
||||
template<typename Callback>
|
||||
void feed(const buffer_c16_t& src, Callback callback) {
|
||||
max_squared = compute_max_squared(src, max_squared);
|
||||
auto src_p = src.p;
|
||||
while(src_p < &src.p[src.count]) {
|
||||
const uint32_t sample = *__SIMD32(src_p)++;
|
||||
const uint32_t mag_sq = __SMUAD(sample, sample);
|
||||
if( mag_sq > max_squared ) {
|
||||
max_squared = mag_sq;
|
||||
}
|
||||
}
|
||||
count += src.count;
|
||||
|
||||
const size_t samples_per_update = src.sampling_rate * update_interval;
|
||||
@ -54,22 +61,6 @@ private:
|
||||
static constexpr float update_interval { 0.1f };
|
||||
uint32_t max_squared { 0 };
|
||||
size_t count { 0 };
|
||||
|
||||
static uint32_t compute_max_squared(
|
||||
const buffer_c16_t& src,
|
||||
uint32_t max_squared
|
||||
) {
|
||||
auto src_p = src.p;
|
||||
while(src_p < &src.p[src.count]) {
|
||||
const uint32_t sample = *__SIMD32(src_p)++;
|
||||
const uint32_t mag_sq = __SMUAD(sample, sample);
|
||||
if( mag_sq > max_squared ) {
|
||||
max_squared = mag_sq;
|
||||
}
|
||||
}
|
||||
|
||||
return max_squared;
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__CHANNEL_STATS_COLLECTOR_H__*/
|
||||
|
158
firmware/baseband/proc_adsbrx.cpp
Normal file
158
firmware/baseband/proc_adsbrx.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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_adsbrx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
using namespace adsb;
|
||||
|
||||
void ADSBRXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
int8_t re, im;
|
||||
float mag;
|
||||
uint32_t c;
|
||||
uint8_t level, bit, byte;
|
||||
bool confidence, first_in_window, last_in_window;
|
||||
|
||||
// This is called at 2M/2048 = 977Hz
|
||||
// One pulse = 500ns = 2 samples
|
||||
// One bit = 2 pulses = 1us = 4 samples
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
// Compute sample's magnitude
|
||||
re = buffer.p[i].real();
|
||||
im = buffer.p[i].imag();
|
||||
mag = __builtin_sqrtf((re * re) + (im * im)) * k;
|
||||
|
||||
// Only used for preamble detection and visualisation
|
||||
level = (mag < 0.3) ? 0 : // Blank weak signals
|
||||
(mag > prev_mag) ? 1 : 0;
|
||||
|
||||
if (decoding) {
|
||||
// Decode
|
||||
|
||||
// 1 bit lasts 2 samples
|
||||
if (sample_count & 1) {
|
||||
if ((prev_mag < threshold_low) && (mag < threshold_low)) {
|
||||
// Both under window, silence.
|
||||
if (null_count > 3) {
|
||||
//text_debug_b.set("Bits:" + bits.substr(0, 25));
|
||||
//text_debug_c.set("Hex:" + hex_str.substr(0, 26));
|
||||
//text_debug_d.set("DF=" + to_string_dec_uint(frame.get_DF()) + " ICAO=" + to_string_hex(frame.get_ICAO_address(), 6));
|
||||
|
||||
const ADSBFrameMessage message(frame);
|
||||
shared_memory.application_queue.push(message);
|
||||
|
||||
decoding = false;
|
||||
} else
|
||||
null_count++;
|
||||
|
||||
confidence = false;
|
||||
if (prev_mag > mag)
|
||||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
|
||||
} else {
|
||||
|
||||
null_count = 0;
|
||||
|
||||
first_in_window = ((prev_mag >= threshold_low) && (prev_mag <= threshold_high));
|
||||
last_in_window = ((mag >= threshold_low) && (mag <= threshold_high));
|
||||
|
||||
if ((first_in_window && !last_in_window) || (!first_in_window && last_in_window)) {
|
||||
confidence = true;
|
||||
if (prev_mag > mag)
|
||||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
} else {
|
||||
confidence = false;
|
||||
if (prev_mag > mag)
|
||||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
byte = bit | (byte << 1);
|
||||
bit_count++;
|
||||
if (!(bit_count & 7)) {
|
||||
// Got one byte
|
||||
frame.push_byte(byte);
|
||||
}
|
||||
}
|
||||
sample_count++;
|
||||
} else {
|
||||
// Look for preamble
|
||||
|
||||
// Shift
|
||||
for (c = 0; c < (ADSB_PREAMBLE_LENGTH - 1); c++)
|
||||
shifter[c] = shifter[c + 1];
|
||||
shifter[15] = std::make_pair(mag, level);
|
||||
|
||||
// Compare
|
||||
for (c = 0; c < ADSB_PREAMBLE_LENGTH; c++) {
|
||||
if (shifter[c].second != adsb_preamble[c])
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == ADSB_PREAMBLE_LENGTH) {
|
||||
decoding = true;
|
||||
sample_count = 0;
|
||||
null_count = 0;
|
||||
bit_count = 0;
|
||||
frame.clear();
|
||||
|
||||
// Compute preamble pulses power to set thresholds
|
||||
threshold = (shifter[0].first + shifter[2].first + shifter[7].first + shifter[9].first) / 4;
|
||||
threshold_high = threshold * 1.414; // +3dB
|
||||
threshold_low = threshold * 0.707; // -3dB
|
||||
}
|
||||
}
|
||||
|
||||
prev_mag = mag;
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBRXProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::ADSBConfigure) {
|
||||
null_count = 0;
|
||||
bit_count = 0;
|
||||
sample_count = 0;
|
||||
decoding = false;
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<ADSBRXProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
62
firmware/baseband/proc_adsbrx.hpp
Normal file
62
firmware/baseband/proc_adsbrx.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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_ADSBRX_H__
|
||||
#define __PROC_ADSBRX_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
#include "adsb_frame.hpp"
|
||||
|
||||
using namespace adsb;
|
||||
|
||||
#define ADSB_PREAMBLE_LENGTH 16
|
||||
|
||||
class ADSBRXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr float k = 1.0f / 128.0f;
|
||||
|
||||
static constexpr size_t baseband_fs = 2000000;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
|
||||
|
||||
ADSBFrame frame { };
|
||||
bool configured { false };
|
||||
float prev_mag { 0 };
|
||||
float threshold, threshold_low, threshold_high;
|
||||
size_t null_count, bit_count, sample_count;
|
||||
std::pair<float, uint8_t> shifter[ADSB_PREAMBLE_LENGTH];
|
||||
bool decoding { };
|
||||
bool preamble { }, active { };
|
||||
uint16_t bit_pos { 0 };
|
||||
uint8_t cur_bit { 0 };
|
||||
uint32_t sample { 0 };
|
||||
int8_t re { }, im { };
|
||||
};
|
||||
|
||||
#endif
|
@ -32,26 +32,29 @@ void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
// This is called at 4M/2048 = 1953Hz
|
||||
// One pulse = 500ns = 2 samples
|
||||
// One bit = 2 pulses = 1us = 4 samples
|
||||
// Test this with ./dump1090 --freq 434000000 --gain 20
|
||||
// Or ./dump1090 --freq 434000000 --gain 20 --interactive --net --net-http-port 8080 --net-beast
|
||||
|
||||
if (!configured) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
if (active) {
|
||||
if (!sample) {
|
||||
sample = 3;
|
||||
|
||||
if (preamble) {
|
||||
if (bit_pos >= 16) {
|
||||
preamble = false;
|
||||
bit_pos = 0;
|
||||
} else {
|
||||
cur_bit = (preamble_parts << bit_pos) >> 15;
|
||||
bit_pos++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!preamble) {
|
||||
/*if (terminate) {
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
buffer.p[i] = { 0, 0 };
|
||||
}
|
||||
terminate--;
|
||||
if (!terminate) {
|
||||
message.done = true;
|
||||
shared_memory.application_queue.push(message);
|
||||
configured = false;
|
||||
return;
|
||||
}
|
||||
} else {*/
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
/*if (active) {
|
||||
if (!sample) {
|
||||
sample = 300;
|
||||
if (bit_pos >= 112) {
|
||||
active = false; // Stop
|
||||
cur_bit = 0;
|
||||
@ -59,38 +62,39 @@ void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
cur_bit = shared_memory.bb_data.data[bit_pos];
|
||||
bit_pos++;
|
||||
}
|
||||
} else
|
||||
sample--;
|
||||
|
||||
if (!preamble) {
|
||||
if (sample == 150)
|
||||
cur_bit ^= 1; // Invert
|
||||
}
|
||||
} else
|
||||
sample--;
|
||||
} else {*/
|
||||
/*cur_bit = 0;
|
||||
if (bit_pos >= 16384) {
|
||||
configured = false;
|
||||
message.done = true;
|
||||
shared_memory.application_queue.push(message);
|
||||
}*/
|
||||
if (bit_pos >= (240 << 1)) {
|
||||
configured = false;
|
||||
terminate = 100;
|
||||
cur_bit = 0;
|
||||
} else {
|
||||
cur_bit = shared_memory.bb_data.data[bit_pos >> 1];
|
||||
bit_pos++;
|
||||
}
|
||||
//}
|
||||
|
||||
if (sample == 1)
|
||||
cur_bit ^= 1; // Invert
|
||||
} else {
|
||||
//cur_bit = 0;
|
||||
if (bit_pos == 8192) { // ?
|
||||
configured = false;
|
||||
message.done = true;
|
||||
shared_memory.application_queue.push(message);
|
||||
if (cur_bit) {
|
||||
// Crude AM
|
||||
buffer.p[i] = am_lut[phase & 3];
|
||||
phase++;
|
||||
} else {
|
||||
buffer.p[i] = { 0, 0 };
|
||||
}
|
||||
bit_pos++;
|
||||
}
|
||||
|
||||
delta = tone_sample * fm_delta;
|
||||
tone_sample += 128;
|
||||
|
||||
if (cur_bit) {
|
||||
phase += delta;
|
||||
sphase = phase + (64 << 24);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]);
|
||||
im = (sine_table_i8[(phase & 0xFF000000U) >> 24]);
|
||||
} else {
|
||||
re = 0;
|
||||
im = 0;
|
||||
}
|
||||
|
||||
buffer.p[i] = {re, im};
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
void ADSBTXProcessor::on_message(const Message* const p) {
|
||||
@ -98,13 +102,10 @@ void ADSBTXProcessor::on_message(const Message* const p) {
|
||||
|
||||
if (message.id == Message::ID::ADSBConfigure) {
|
||||
bit_pos = 0;
|
||||
sample = 0;
|
||||
cur_bit = 0;
|
||||
preamble = true;
|
||||
phase = 0;
|
||||
active = true;
|
||||
configured = true;
|
||||
|
||||
fm_delta = 50000 * (0xFFFFFFULL / 4000000); // Fixed bw for now
|
||||
terminate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,6 @@
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
#define TEST_F2D(f) (uint32_t)((f) * ((1ULL << 32) / 4000000))
|
||||
|
||||
class ADSBTXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
@ -39,16 +37,18 @@ private:
|
||||
|
||||
BasebandThread baseband_thread { 4000000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
const uint16_t preamble_parts = 0b1010000101000000;
|
||||
bool preamble { }, active { };
|
||||
uint16_t bit_pos { 0 };
|
||||
uint8_t cur_bit { 0 };
|
||||
uint32_t sample { 0 };
|
||||
uint32_t tone_phase { 0 };
|
||||
uint32_t fm_delta { 0 };
|
||||
uint32_t phase { 0 }, sphase { 0 };
|
||||
int32_t tone_sample { 0 }, delta { 0 };
|
||||
int8_t re { }, im { };
|
||||
const complex8_t am_lut[4] = {
|
||||
{ 127, 0 },
|
||||
{ 0, 127 },
|
||||
{ -127, 0 },
|
||||
{ 0, -127 }
|
||||
};
|
||||
|
||||
bool active { };
|
||||
uint32_t terminate { };
|
||||
uint32_t bit_pos { 0 };
|
||||
uint32_t cur_bit { 0 };
|
||||
uint32_t phase { 0 };
|
||||
|
||||
TXDoneMessage message { };
|
||||
};
|
||||
|
@ -129,7 +129,7 @@ typedef enum IRQn {
|
||||
|
||||
#define __SIMD32_TYPE int32_t
|
||||
#define __SIMD32(addr) (*(__SIMD32_TYPE **) & (addr))
|
||||
#define _SIMD32_OFFSET(addr) (*(__SIMD32_TYPE *) (addr))
|
||||
#define _SIMD32_OFFSET(addr) (*(__SIMD32_TYPE *) (addr))
|
||||
|
||||
/* Overload of __SXTB16() to add ROR argument, since using __ROR() as an
|
||||
* argument to the existing __SXTB16() doesn't produce optimum/sane code.
|
||||
|
177
firmware/common/adsb.cpp
Normal file
177
firmware/common/adsb.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 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 "adsb.hpp"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
namespace adsb {
|
||||
|
||||
void make_frame_mode_s(ADSBFrame& frame, const uint32_t ICAO_address) {
|
||||
frame.clear();
|
||||
frame.push_byte((17 << 3) | 5); // DF17 and CA
|
||||
frame.push_byte(ICAO_address >> 16);
|
||||
frame.push_byte(ICAO_address >> 8);
|
||||
frame.push_byte(ICAO_address & 0xFF);
|
||||
}
|
||||
|
||||
void generate_frame_id(ADSBFrame& frame, const uint32_t ICAO_address, const std::string& callsign) {
|
||||
std::string callsign_formatted(8, '_');
|
||||
uint64_t callsign_coded = 0;
|
||||
uint32_t c, s;
|
||||
char ch;
|
||||
|
||||
make_frame_mode_s(frame, ICAO_address);
|
||||
|
||||
frame.push_byte(4 << 3); // TC = 4: Aircraft ID
|
||||
|
||||
// Translate and encode callsign
|
||||
for (c = 0; c < 8; c++) {
|
||||
ch = callsign[c];
|
||||
|
||||
for (s = 0; s < 64; s++)
|
||||
if (ch == icao_id_lut[s]) break;
|
||||
|
||||
if (s == 64) {
|
||||
ch = ' '; // Invalid character
|
||||
s = 32;
|
||||
}
|
||||
|
||||
callsign_coded <<= 6;
|
||||
callsign_coded |= s;
|
||||
|
||||
//callsign[c] = ch;
|
||||
}
|
||||
|
||||
// Insert callsign in frame
|
||||
for (c = 0; c < 6; c++)
|
||||
frame.push_byte((callsign_coded >> ((5 - c) * 8)) & 0xFF);
|
||||
|
||||
frame.make_CRC();
|
||||
}
|
||||
|
||||
/*void generate_frame_emergency(ADSBFrame& frame, const uint32_t ICAO_address, const uint8_t code) {
|
||||
make_frame_mode_s(frame, ICAO_address);
|
||||
|
||||
frame.push_byte((28 << 3) + 1); // TC = 28 (Emergency), subtype = 1 (Emergency)
|
||||
frame.push_byte(code << 5);
|
||||
|
||||
frame.make_CRC();
|
||||
}*/
|
||||
|
||||
void generate_frame_identity(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t code) {
|
||||
frame.clear();
|
||||
|
||||
frame.push_byte(21 << 3); // DF = 21
|
||||
frame.push_byte(0);
|
||||
|
||||
// 1337:
|
||||
frame.push_byte(0b00011100);
|
||||
frame.push_byte(0b00111101);
|
||||
|
||||
frame.make_CRC();
|
||||
}
|
||||
|
||||
float cpr_mod(float a, float b) {
|
||||
return a - (b * floor(a / b));
|
||||
}
|
||||
|
||||
int cpr_NL(float lat) {
|
||||
if (lat < 0)
|
||||
lat = -lat; // Symmetry
|
||||
|
||||
for (size_t c = 0; c < 58; c++) {
|
||||
if (lat < adsb_lat_lut[c])
|
||||
return 59 - c;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cpr_N(float lat, int is_odd) {
|
||||
int nl = cpr_NL(lat) - is_odd;
|
||||
|
||||
if (nl < 1)
|
||||
nl = 1;
|
||||
|
||||
return nl;
|
||||
}
|
||||
|
||||
void generate_frame_pos(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t altitude,
|
||||
const float latitude, const float longitude, const uint32_t time_parity) {
|
||||
uint32_t altitude_coded;
|
||||
uint32_t lat, lon;
|
||||
float delta_lat, yz, rlat, delta_lon, xz;
|
||||
|
||||
make_frame_mode_s(frame, ICAO_address);
|
||||
|
||||
frame.push_byte(11 << 3); // TC = 11 (Airborne position)
|
||||
|
||||
altitude_coded = (altitude + 1000) / 25; // Can be coded in 100ft steps also
|
||||
frame.push_byte(altitude_coded >> 4); // Top-most altitude bits
|
||||
|
||||
// Decoding method (from dump1090):
|
||||
// index int j = floor(((59 * latcprE - 60 * latcprO) / 131072) + 0.50)
|
||||
// latE = DlatE * (cpr_mod(j, 60) + (latcprE / 131072))
|
||||
// latO = DlatO * (cpr_mod(j, 59) + (latcprO / 131072))
|
||||
// if latE >= 270 -> latE -= 360
|
||||
// if latO >= 270 -> latO -= 360
|
||||
// if (cpr_NL(latE) != cpr_NL(latO)) return;
|
||||
|
||||
// int ni = cpr_N(latE ,0);
|
||||
// int m = floor((((loncprE * (cpr_NL(latE) - 1)) - (loncprO * cpr_NL(latE))) / 131072) + 0.5)
|
||||
// lon = cpr_Dlon(latE, 0) * (cpr_mod(m, ni) + loncprE / 131072);
|
||||
// lat = latE;
|
||||
// ... or ...
|
||||
// int ni = cpr_N(latO ,0);
|
||||
// int m = floor((((loncprE * (cpr_NL(latO) - 1)) - (loncprO * cpr_NL(latO))) / 131072) + 0.5)
|
||||
// lon = cpr_Dlon(latO, 0) * (cpr_mod(m, ni) + loncprO / 131072);
|
||||
// lat = latO;
|
||||
// ... and ...
|
||||
// if (lon > 180) lon -= 360;
|
||||
|
||||
// CPR encoding
|
||||
// Info: http://antena.fe.uni-lj.si/literatura/Razno/Avionika/modes/CPRencoding.pdf
|
||||
|
||||
delta_lat = 360.0 / ((4.0 * 15.0) - time_parity); // NZ = 15
|
||||
yz = floor(131072.0 * (cpr_mod(latitude, delta_lat) / delta_lat) + 0.5);
|
||||
rlat = delta_lat * ((yz / 131072.0) + floor(latitude / delta_lat));
|
||||
|
||||
if ((cpr_NL(rlat) - time_parity) > 0)
|
||||
delta_lon = 360.0 / cpr_N(rlat, time_parity);
|
||||
else
|
||||
delta_lon = 360.0;
|
||||
xz = floor(131072.0 * (cpr_mod(longitude, delta_lon) / delta_lon) + 0.5);
|
||||
|
||||
lat = cpr_mod(yz, 131072.0);
|
||||
lon = cpr_mod(xz, 131072.0);
|
||||
|
||||
frame.push_byte((altitude_coded << 4) | ((uint32_t)time_parity << 2) | (lat >> 15));
|
||||
frame.push_byte(lat >> 7);
|
||||
frame.push_byte((lat << 1) | (lon >> 16));
|
||||
frame.push_byte(lon >> 8);
|
||||
frame.push_byte(lon);
|
||||
|
||||
frame.make_CRC();
|
||||
}
|
||||
|
||||
} /* namespace adsb */
|
60
firmware/common/adsb.hpp
Normal file
60
firmware/common/adsb.hpp
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 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 __ADSB_H__
|
||||
#define __ADSB_H__
|
||||
|
||||
#include "adsb_frame.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace adsb {
|
||||
|
||||
const float adsb_lat_lut[58] = {
|
||||
10.47047130, 14.82817437, 18.18626357, 21.02939493,
|
||||
23.54504487, 25.82924707, 27.93898710, 29.91135686,
|
||||
31.77209708, 33.53993436, 35.22899598, 36.85025108,
|
||||
38.41241892, 39.92256684, 41.38651832, 42.80914012,
|
||||
44.19454951, 45.54626723, 46.86733252, 48.16039128,
|
||||
49.42776439, 50.67150166, 51.89342469, 53.09516153,
|
||||
54.27817472, 55.44378444, 56.59318756, 57.72747354,
|
||||
58.84763776, 59.95459277, 61.04917774, 62.13216659,
|
||||
63.20427479, 64.26616523, 65.31845310, 66.36171008,
|
||||
67.39646774, 68.42322022, 69.44242631, 70.45451075,
|
||||
71.45986473, 72.45884545, 73.45177442, 74.43893416,
|
||||
75.42056257, 76.39684391, 77.36789461, 78.33374083,
|
||||
79.29428225, 80.24923213, 81.19801349, 82.13956981,
|
||||
83.07199445, 83.99173563, 84.89166191, 85.75541621,
|
||||
86.53536998, 87.00000000
|
||||
};
|
||||
|
||||
void make_frame_mode_s(ADSBFrame& frame, const uint32_t ICAO_address);
|
||||
void generate_frame_id(ADSBFrame& frame, const uint32_t ICAO_address, const std::string& callsign);
|
||||
void generate_frame_pos(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t altitude,
|
||||
const float latitude, const float longitude, const uint32_t time_parity);
|
||||
void generate_frame_emergency(ADSBFrame& frame, const uint32_t ICAO_address, const uint8_t code);
|
||||
void generate_frame_identity(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t code);
|
||||
|
||||
} /* namespace adsb */
|
||||
|
||||
#endif/*__ADSB_H__*/
|
27
firmware/common/adsb_frame.cpp
Normal file
27
firmware/common/adsb_frame.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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 "adsb_frame.hpp"
|
||||
|
||||
namespace adsb {
|
||||
|
||||
} /* namespace adsb */
|
120
firmware/common/adsb_frame.hpp
Normal file
120
firmware/common/adsb_frame.hpp
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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 __ADSB_FRAME_H__
|
||||
#define __ADSB_FRAME_H__
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace adsb {
|
||||
|
||||
alignas(4) const uint8_t adsb_preamble[16] = { 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0 };
|
||||
alignas(4) const char icao_id_lut[65] = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######";
|
||||
|
||||
class ADSBFrame {
|
||||
public:
|
||||
uint8_t get_DF() {
|
||||
return (raw_data[0] >> 3);
|
||||
}
|
||||
|
||||
uint8_t get_msg_type() {
|
||||
return (raw_data[4] >> 3);
|
||||
}
|
||||
|
||||
uint32_t get_ICAO_address() {
|
||||
return (raw_data[1] << 16) + (raw_data[2] << 8) + raw_data[3];
|
||||
}
|
||||
|
||||
void clear() {
|
||||
index = 0;
|
||||
memset(raw_data, 0, 14);
|
||||
}
|
||||
|
||||
void push_byte(uint8_t byte) {
|
||||
if (index >= 14)
|
||||
return;
|
||||
|
||||
raw_data[index++] = byte;
|
||||
}
|
||||
|
||||
uint8_t * get_raw_data() {
|
||||
return raw_data;
|
||||
}
|
||||
|
||||
std::string get_callsign() {
|
||||
uint64_t callsign_coded = 0;
|
||||
uint32_t c;
|
||||
std::string callsign = "";
|
||||
|
||||
// Frame bytes to long
|
||||
for (c = 5; c < 11; c++) {
|
||||
callsign_coded <<= 8;
|
||||
callsign_coded |= raw_data[c];
|
||||
}
|
||||
|
||||
// Long to 6-bit characters
|
||||
for (c = 0; c < 8; c++) {
|
||||
callsign.append(1, icao_id_lut[(callsign_coded >> 42) & 0x3F]);
|
||||
callsign_coded <<= 6;
|
||||
}
|
||||
|
||||
return callsign;
|
||||
}
|
||||
|
||||
void make_CRC() {
|
||||
uint8_t adsb_crc[14]; // Temp buffer
|
||||
uint8_t b, c, s, bitn;
|
||||
const uint32_t crc_poly = 0x1205FFF;
|
||||
|
||||
// Clear CRC
|
||||
raw_data[11] = 0x00;
|
||||
raw_data[12] = 0x00;
|
||||
raw_data[13] = 0x00;
|
||||
|
||||
// Compute CRC
|
||||
memcpy(adsb_crc, raw_data, 14);
|
||||
for (c = 0; c < 11; c++) {
|
||||
for (b = 0; b < 8; b++) {
|
||||
if ((adsb_crc[c] << b) & 0x80) {
|
||||
for (s = 0; s < 25; s++) {
|
||||
bitn = (c * 8) + b + s;
|
||||
if ((crc_poly >> s) & 1) adsb_crc[bitn >> 3] ^= (0x80 >> (bitn & 7));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert CRC in frame
|
||||
memcpy(&raw_data[11], &adsb_crc[11], 3);
|
||||
}
|
||||
|
||||
private:
|
||||
static const uint8_t adsb_preamble[16];
|
||||
static const char icao_id_lut[65];
|
||||
alignas(4) uint8_t index { 0 };
|
||||
alignas(4) uint8_t raw_data[14] { }; // 112 bits at most
|
||||
};
|
||||
|
||||
} /* namespace adsb */
|
||||
|
||||
#endif/*__ADSB_FRAME_H__*/
|
@ -35,6 +35,7 @@
|
||||
#include "ert_packet.hpp"
|
||||
#include "tpms_packet.hpp"
|
||||
#include "pocsag_packet.hpp"
|
||||
#include "adsb_frame.hpp"
|
||||
#include "jammer.hpp"
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
@ -91,6 +92,7 @@ public:
|
||||
SigGenTone = 44,
|
||||
|
||||
POCSAGPacket = 50,
|
||||
ADSBFrame = 51,
|
||||
|
||||
RequestSignal = 52,
|
||||
FIFOData = 53,
|
||||
@ -317,6 +319,18 @@ public:
|
||||
pocsag::POCSAGPacket packet;
|
||||
};
|
||||
|
||||
class ADSBFrameMessage : public Message {
|
||||
public:
|
||||
constexpr ADSBFrameMessage(
|
||||
const adsb::ADSBFrame& frame
|
||||
) : Message { ID::ADSBFrame },
|
||||
frame { frame }
|
||||
{
|
||||
}
|
||||
|
||||
adsb::ADSBFrame frame;
|
||||
};
|
||||
|
||||
class ShutdownMessage : public Message {
|
||||
public:
|
||||
constexpr ShutdownMessage(
|
||||
|
@ -26,8 +26,6 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include "optional.hpp"
|
||||
|
||||
#include "baseband.hpp"
|
||||
|
||||
namespace pocsag {
|
||||
|
@ -63,6 +63,7 @@ private:
|
||||
char c[4];
|
||||
};
|
||||
|
||||
constexpr image_tag_t image_tag_adsb_rx { 'P', 'A', 'D', 'R' };
|
||||
constexpr image_tag_t image_tag_ais { 'P', 'A', 'I', 'S' };
|
||||
constexpr image_tag_t image_tag_am_audio { 'P', 'A', 'M', 'A' };
|
||||
constexpr image_tag_t image_tag_capture { 'P', 'C', 'A', 'P' };
|
||||
@ -79,7 +80,7 @@ constexpr image_tag_t image_tag_afsk { 'P', 'A', 'F', 'S' };
|
||||
constexpr image_tag_t image_tag_tones { 'P', 'T', 'O', 'N' };
|
||||
constexpr image_tag_t image_tag_rds { 'P', 'R', 'D', 'S' };
|
||||
constexpr image_tag_t image_tag_ook { 'P', 'O', 'O', 'K' };
|
||||
constexpr image_tag_t image_tag_adsb_tx { 'P', 'A', 'D', 'S' };
|
||||
constexpr image_tag_t image_tag_adsb_tx { 'P', 'A', 'D', 'T' };
|
||||
constexpr image_tag_t image_tag_replay { 'P', 'R', 'E', 'P' };
|
||||
constexpr image_tag_t image_tag_fsktx { 'P', 'F', 'S', 'K' };
|
||||
constexpr image_tag_t image_tag_mic_tx { 'P', 'M', 'T', 'X' };
|
||||
|
Loading…
Reference in New Issue
Block a user