mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-04-16 11:31:27 +00: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
|
debounce.cpp
|
||||||
touch.cpp
|
touch.cpp
|
||||||
touch_adc.cpp
|
touch_adc.cpp
|
||||||
protocols/adsb.cpp
|
|
||||||
protocols/aprs.cpp
|
protocols/aprs.cpp
|
||||||
protocols/ax25.cpp
|
protocols/ax25.cpp
|
||||||
protocols/bht.cpp
|
protocols/bht.cpp
|
||||||
@ -217,6 +216,8 @@ set(CPPSRC
|
|||||||
${COMMON}/pocsag_packet.cpp
|
${COMMON}/pocsag_packet.cpp
|
||||||
${COMMON}/pocsag.cpp
|
${COMMON}/pocsag.cpp
|
||||||
${COMMON}/morse.cpp
|
${COMMON}/morse.cpp
|
||||||
|
${COMMON}/adsb_frame.cpp
|
||||||
|
${COMMON}/adsb.cpp
|
||||||
ais_app.cpp
|
ais_app.cpp
|
||||||
tpms_app.cpp
|
tpms_app.cpp
|
||||||
pocsag_app.cpp
|
pocsag_app.cpp
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
|
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
|
||||||
|
|
||||||
//TODO: De bruijn sequence scanner for encoders
|
//TODO: De bruijn sequence scanner for encoders
|
||||||
//TODO: Waveform viewer
|
|
||||||
//TEST: Mic tx
|
//TEST: Mic tx
|
||||||
//TEST: Menuview refresh, seems to blink a lot
|
//TEST: Menuview refresh, seems to blink a lot
|
||||||
//TEST: Check AFSK transmit end, skips last bits ?
|
//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 "adsb.hpp"
|
||||||
#include "string_format.hpp"
|
#include "string_format.hpp"
|
||||||
#include "sine_table_int8.hpp"
|
|
||||||
#include "portapack.hpp"
|
#include "portapack.hpp"
|
||||||
#include "baseband_api.hpp"
|
#include "baseband_api.hpp"
|
||||||
#include "portapack_persistent_memory.hpp"
|
#include "portapack_persistent_memory.hpp"
|
||||||
@ -39,25 +38,35 @@ using namespace portapack;
|
|||||||
|
|
||||||
namespace ui {
|
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() {
|
void ADSBRxView::focus() {
|
||||||
offset_field.focus();
|
offset_field.focus();
|
||||||
offset_field.set_value(13179);
|
offset_field.set_value(13179);
|
||||||
}
|
}
|
||||||
|
|
||||||
ADSBRxView::~ADSBRxView() {
|
ADSBRxView::~ADSBRxView() {
|
||||||
//transmitter_model.disable();
|
receiver_model.disable();
|
||||||
//baseband::shutdown();
|
baseband::shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ADSBRxView::analyze(uint64_t offset) {
|
/*bool ADSBRxView::analyze(uint64_t offset) {
|
||||||
Coord lcd_x = 0, lcd_y = 0;
|
Coord lcd_x = 0, lcd_y = 0;
|
||||||
|
int8_t re, im;
|
||||||
adsb_frame frame;
|
adsb_frame frame;
|
||||||
int16_t file_data[128]; // 256 bytes / 2 IQ / 16 bits = 64 samples
|
int16_t file_data[128]; // 256 bytes / 2 IQ / 16 bits = 64 samples
|
||||||
complex8_t iq_data[256]; // 256 samples
|
complex8_t iq_data[256]; // 256 samples
|
||||||
uint64_t file_offset = 0;
|
uint64_t file_offset = 0;
|
||||||
uint8_t data_put = 0, data_get = 0;
|
uint8_t data_put = 0, data_get = 0;
|
||||||
int16_t f_re, f_im;
|
int16_t f_re, f_im;
|
||||||
int8_t re, im;
|
|
||||||
uint32_t c;
|
uint32_t c;
|
||||||
uint8_t level, bit, byte;
|
uint8_t level, bit, byte;
|
||||||
Color mark_color;
|
Color mark_color;
|
||||||
@ -224,42 +233,64 @@ bool ADSBRxView::analyze(uint64_t offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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) {
|
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({
|
add_children({
|
||||||
&labels,
|
//&labels,
|
||||||
&offset_field,
|
&offset_field,
|
||||||
&button_ffw,
|
//&button_ffw,
|
||||||
&text_debug_a,
|
&text_debug_a,
|
||||||
&text_debug_b,
|
&text_debug_b,
|
||||||
&text_debug_c,
|
&text_debug_c,
|
||||||
&text_debug_d,
|
&recent_entries_view
|
||||||
&text_debug_e
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 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()) {
|
if (result.is_valid()) {
|
||||||
text_debug_a.set("Can't open file");
|
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
|
// TODO
|
||||||
};
|
};*/
|
||||||
|
|
||||||
button_ffw.on_select = [this, &nav](Button&) {
|
/*button_ffw.on_select = [this, &nav](Button&) {
|
||||||
auto new_view = nav.push<GeoMapView>();
|
//nav.push<GeoMapView>();
|
||||||
/*while (!analyze(f_offset)) {
|
while (!analyze(f_offset)) {
|
||||||
f_offset++;
|
f_offset++;
|
||||||
}
|
}
|
||||||
offset_field.set_value(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 */
|
} /* namespace ui */
|
||||||
|
@ -26,12 +26,48 @@
|
|||||||
//#include "ui_widget.hpp"
|
//#include "ui_widget.hpp"
|
||||||
#include "ui_navigation.hpp"
|
#include "ui_navigation.hpp"
|
||||||
#include "ui_font_fixed_8x16.hpp"
|
#include "ui_font_fixed_8x16.hpp"
|
||||||
|
#include "recent_entries.hpp"
|
||||||
|
|
||||||
#include "message.hpp"
|
#include "message.hpp"
|
||||||
#include "portapack.hpp"
|
#include "portapack.hpp"
|
||||||
|
|
||||||
namespace ui {
|
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 {
|
class ADSBRxView : public View {
|
||||||
public:
|
public:
|
||||||
ADSBRxView(NavigationView& nav);
|
ADSBRxView(NavigationView& nav);
|
||||||
@ -42,16 +78,24 @@ public:
|
|||||||
std::string title() const override { return "ADS-B debug"; };
|
std::string title() const override { return "ADS-B debug"; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr float k = 1.0f / 128.0f;
|
//static constexpr float k = 1.0f / 128.0f;
|
||||||
|
|
||||||
File iq_file { };
|
//File iq_file { };
|
||||||
size_t f_offset { 0 };
|
//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() }
|
{ { 0 * 8, 0 * 8 }, "Test", Color::light_grey() }
|
||||||
};
|
};*/
|
||||||
|
|
||||||
NumberField offset_field {
|
NumberField offset_field {
|
||||||
{ 0, 0 },
|
{ 0, 0 },
|
||||||
@ -60,10 +104,6 @@ private:
|
|||||||
1,
|
1,
|
||||||
'0'
|
'0'
|
||||||
};
|
};
|
||||||
Text text_debug_e {
|
|
||||||
{ 7 * 8, 0 * 16, 14 * 8, 16 },
|
|
||||||
"-"
|
|
||||||
};
|
|
||||||
|
|
||||||
Text text_debug_a {
|
Text text_debug_a {
|
||||||
{ 0 * 8, 1 * 16, 30 * 8, 16 },
|
{ 0 * 8, 1 * 16, 30 * 8, 16 },
|
||||||
@ -77,24 +117,19 @@ private:
|
|||||||
{ 0 * 8, 3 * 16, 30 * 8, 16 },
|
{ 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 },
|
{ 184, 0 * 16, 56, 16 },
|
||||||
"FFW"
|
"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 */
|
} /* namespace ui */
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include "string_format.hpp"
|
#include "string_format.hpp"
|
||||||
#include "portapack.hpp"
|
#include "portapack.hpp"
|
||||||
#include "baseband_api.hpp"
|
#include "baseband_api.hpp"
|
||||||
#include "portapack_persistent_memory.hpp"
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -37,7 +36,6 @@ using namespace portapack;
|
|||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
void ADSBTxView::focus() {
|
void ADSBTxView::focus() {
|
||||||
//sym_icao.focus();
|
|
||||||
tx_view.focus();
|
tx_view.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,28 +44,38 @@ ADSBTxView::~ADSBTxView() {
|
|||||||
baseband::shutdown();
|
baseband::shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADSBTxView::paint(Painter& painter) {
|
void ADSBTxView::paint(Painter&) {
|
||||||
(void)painter;
|
|
||||||
button_callsign.set_text(callsign);
|
button_callsign.set_text(callsign);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADSBTxView::generate_frame() {
|
void ADSBTxView::generate_frame() {
|
||||||
uint32_t c;
|
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)
|
// Convert to binary (1 byte per bit, faster for baseband code)
|
||||||
for (c = 0; c < 112; c++) {
|
for (c = 0; c < 112; c++) {
|
||||||
if ((frame.get_byte(c >> 3) << (c & 7)) & 0x80)
|
if ((raw_ptr[c >> 3] << (c & 7)) & 0x80) {
|
||||||
adsb_bin[c] = 1;
|
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
|
// Display in hex for debug
|
||||||
//text_frame_a.set(to_string_hex_array(frame.get_byte(0), 7));
|
text_frame_a.set(to_string_hex_array(frames[0].get_raw_data(), 7));
|
||||||
//text_frame_b.set(to_string_hex_array(frame.get_byte(7), 7));
|
text_frame_b.set(to_string_hex_array(frames[0].get_raw_data() + 7, 7));
|
||||||
|
|
||||||
button_callsign.set_text(callsign);
|
button_callsign.set_text(callsign);
|
||||||
}
|
}
|
||||||
@ -75,28 +83,78 @@ void ADSBTxView::generate_frame() {
|
|||||||
bool ADSBTxView::start_tx() {
|
bool ADSBTxView::start_tx() {
|
||||||
generate_frame();
|
generate_frame();
|
||||||
|
|
||||||
memcpy(shared_memory.bb_data.data, adsb_bin, 112);
|
transmitter_model.set_tuning_frequency(434000000);
|
||||||
baseband::set_adsb();
|
|
||||||
|
|
||||||
transmitter_model.set_tuning_frequency(434000000); // DEBUG
|
|
||||||
transmitter_model.set_sampling_rate(4000000U);
|
transmitter_model.set_sampling_rate(4000000U);
|
||||||
transmitter_model.set_rf_amp(true);
|
transmitter_model.set_rf_amp(true);
|
||||||
transmitter_model.set_vga(40);
|
transmitter_model.set_vga(40);
|
||||||
transmitter_model.set_baseband_bandwidth(2500000);
|
transmitter_model.set_baseband_bandwidth(10000000);
|
||||||
transmitter_model.enable();
|
transmitter_model.enable();
|
||||||
|
|
||||||
return false; // DEBUG
|
baseband::set_adsb();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADSBTxView::on_txdone(const bool v) {
|
void ADSBTxView::on_txdone(const bool v) {
|
||||||
//size_t sr;
|
|
||||||
|
|
||||||
if (v) {
|
if (v) {
|
||||||
transmitter_model.disable();
|
transmitter_model.disable();
|
||||||
tx_view.set_transmitting(false);
|
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.set_by_value(17); // Mode S
|
||||||
|
|
||||||
options_format.on_change = [this](size_t i, int32_t v) {
|
options_format.on_change = [this](size_t, int32_t) {
|
||||||
(void)i;
|
|
||||||
(void)v;
|
|
||||||
generate_frame();
|
generate_frame();
|
||||||
};
|
};
|
||||||
sym_icao.on_change = [this]() {
|
sym_icao.on_change = [this]() {
|
||||||
generate_frame();
|
generate_frame();
|
||||||
};
|
};
|
||||||
button_callsign.on_select = [this, &nav](Button&) {
|
button_callsign.on_select = [this, &nav](Button&) {
|
||||||
text_prompt(nav, &callsign, 9);
|
text_prompt(nav, &callsign, 8);
|
||||||
};
|
};
|
||||||
|
|
||||||
field_altitude.set_value(11000);
|
field_altitude.set_value(11000);
|
||||||
@ -146,19 +202,24 @@ ADSBTxView::ADSBTxView(NavigationView& nav) {
|
|||||||
field_lon_minutes.set_value(0);
|
field_lon_minutes.set_value(0);
|
||||||
field_lon_seconds.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++)
|
for (c = 0; c < 4; c++)
|
||||||
field_squawk.set_sym(c, 0);
|
field_squawk.set_sym(c, 0);
|
||||||
|
|
||||||
generate_frame();
|
generate_frame();
|
||||||
|
|
||||||
tx_view.on_edit_frequency = [this, &nav]() {
|
receiver_model.set_tuning_frequency(434000000); // DEBUG
|
||||||
// Shouldn't be able to edit frequency
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
tx_view.on_start = [this]() {
|
tx_view.on_start = [this]() {
|
||||||
if (start_tx())
|
if (start_tx())
|
||||||
tx_view.set_transmitting(true);
|
tx_view.set_transmitting(true);
|
||||||
|
//rotate_frames();
|
||||||
};
|
};
|
||||||
|
|
||||||
tx_view.on_stop = [this]() {
|
tx_view.on_stop = [this]() {
|
||||||
|
@ -41,7 +41,7 @@ public:
|
|||||||
ADSBTxView(NavigationView& nav);
|
ADSBTxView(NavigationView& nav);
|
||||||
~ADSBTxView();
|
~ADSBTxView();
|
||||||
|
|
||||||
void paint(Painter& painter) override;
|
void paint(Painter&) override;
|
||||||
|
|
||||||
void focus() override;
|
void focus() override;
|
||||||
|
|
||||||
@ -54,16 +54,44 @@ private:
|
|||||||
SEQUENCE
|
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;
|
tx_modes tx_mode = IDLE;
|
||||||
|
|
||||||
std::string callsign = "KLM1023 ";
|
std::string callsign = "PORTAPAC";
|
||||||
|
|
||||||
adsb_frame frame { };
|
ADSBFrame frames[4] { };
|
||||||
uint8_t adsb_bin[112]; // 112 bit data block
|
|
||||||
|
|
||||||
bool start_tx();
|
bool start_tx();
|
||||||
void generate_frame();
|
void generate_frame();
|
||||||
void generate_frame_pos();
|
void rotate_frames();
|
||||||
void on_txdone(const bool v);
|
void on_txdone(const bool v);
|
||||||
|
|
||||||
const Style style_val {
|
const Style style_val {
|
||||||
@ -82,8 +110,8 @@ private:
|
|||||||
{ { 2 * 8, 4 * 8 }, "ICAO24:", Color::light_grey() },
|
{ { 2 * 8, 4 * 8 }, "ICAO24:", Color::light_grey() },
|
||||||
{ { 2 * 8, 7 * 8 }, "ID:", Color::light_grey() },
|
{ { 2 * 8, 7 * 8 }, "ID:", Color::light_grey() },
|
||||||
{ { 2 * 8, 10 * 8 }, "Altitude: feet", 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, 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, 14 * 8 }, "Longitude: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font
|
||||||
{ { 15 * 8, 18 * 8 }, "Squawk", Color::light_grey() }
|
{ { 15 * 8, 18 * 8 }, "Squawk", Color::light_grey() }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,23 +145,23 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
NumberField field_lat_degrees {
|
NumberField field_lat_degrees {
|
||||||
{ 12 * 8, 6 * 16 }, 3, { -90, 90 }, 1, ' '
|
{ 12 * 8, 6 * 16 }, 4, { -90, 90 }, 1, ' '
|
||||||
};
|
};
|
||||||
NumberField field_lat_minutes {
|
NumberField field_lat_minutes {
|
||||||
{ 16 * 8, 6 * 16 }, 2, { 0, 59 }, 1, ' '
|
{ 17 * 8, 6 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||||
};
|
};
|
||||||
NumberField field_lat_seconds {
|
NumberField field_lat_seconds {
|
||||||
{ 19 * 8, 6 * 16 }, 2, { 0, 59 }, 1, ' '
|
{ 20 * 8, 6 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||||
};
|
};
|
||||||
|
|
||||||
NumberField field_lon_degrees {
|
NumberField field_lon_degrees {
|
||||||
{ 12 * 8, 7 * 16 }, 3, { -90, 90 }, 1, ' '
|
{ 12 * 8, 7 * 16 }, 4, { -180, 180 }, 1, ' '
|
||||||
};
|
};
|
||||||
NumberField field_lon_minutes {
|
NumberField field_lon_minutes {
|
||||||
{ 16 * 8, 7 * 16 }, 2, { 0, 59 }, 1, ' '
|
{ 17 * 8, 7 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||||
};
|
};
|
||||||
NumberField field_lon_seconds {
|
NumberField field_lon_seconds {
|
||||||
{ 19 * 8, 7 * 16 }, 2, { 0, 59 }, 1, ' '
|
{ 20 * 8, 7 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||||
};
|
};
|
||||||
|
|
||||||
Checkbox check_emergency {
|
Checkbox check_emergency {
|
||||||
@ -160,8 +188,8 @@ private:
|
|||||||
|
|
||||||
TransmitterView tx_view {
|
TransmitterView tx_view {
|
||||||
16 * 16,
|
16 * 16,
|
||||||
1090000000,
|
0,
|
||||||
2000000,
|
0,
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ void TransmitterView::on_tuning_frequency_changed(rf::Frequency f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TransmitterView::on_bandwidth_changed(uint32_t bandwidth) {
|
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) {
|
void TransmitterView::set_transmitting(const bool transmitting) {
|
||||||
@ -92,7 +92,7 @@ void TransmitterView::set_transmitting(const bool transmitting) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TransmitterView::on_show() {
|
void TransmitterView::on_show() {
|
||||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
//field_frequency.set_value(receiver_model.tuning_frequency());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransmitterView::focus() {
|
void TransmitterView::focus() {
|
||||||
|
@ -275,12 +275,19 @@ macro(DeclareTargets chunk_tag name)
|
|||||||
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img)
|
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img)
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
### ADS-B RX
|
||||||
|
|
||||||
|
set(MODE_CPPSRC
|
||||||
|
proc_adsbrx.cpp
|
||||||
|
)
|
||||||
|
DeclareTargets(PADR adsbrx)
|
||||||
|
|
||||||
### ADS-B TX
|
### ADS-B TX
|
||||||
|
|
||||||
set(MODE_CPPSRC
|
set(MODE_CPPSRC
|
||||||
proc_adsbtx.cpp
|
proc_adsbtx.cpp
|
||||||
)
|
)
|
||||||
DeclareTargets(PADS ads)
|
DeclareTargets(PADT adsbtx)
|
||||||
|
|
||||||
### AFSK
|
### AFSK
|
||||||
|
|
||||||
|
@ -35,7 +35,14 @@ class ChannelStatsCollector {
|
|||||||
public:
|
public:
|
||||||
template<typename Callback>
|
template<typename Callback>
|
||||||
void feed(const buffer_c16_t& src, Callback 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;
|
count += src.count;
|
||||||
|
|
||||||
const size_t samples_per_update = src.sampling_rate * update_interval;
|
const size_t samples_per_update = src.sampling_rate * update_interval;
|
||||||
@ -54,22 +61,6 @@ private:
|
|||||||
static constexpr float update_interval { 0.1f };
|
static constexpr float update_interval { 0.1f };
|
||||||
uint32_t max_squared { 0 };
|
uint32_t max_squared { 0 };
|
||||||
size_t count { 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__*/
|
#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
|
// This is called at 4M/2048 = 1953Hz
|
||||||
// One pulse = 500ns = 2 samples
|
// One pulse = 500ns = 2 samples
|
||||||
// One bit = 2 pulses = 1us = 4 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;
|
if (!configured) return;
|
||||||
|
|
||||||
for (size_t i = 0; i < buffer.count; i++) {
|
/*if (terminate) {
|
||||||
|
for (size_t i = 0; i < buffer.count; i++) {
|
||||||
if (active) {
|
buffer.p[i] = { 0, 0 };
|
||||||
if (!sample) {
|
}
|
||||||
sample = 3;
|
terminate--;
|
||||||
|
if (!terminate) {
|
||||||
if (preamble) {
|
message.done = true;
|
||||||
if (bit_pos >= 16) {
|
shared_memory.application_queue.push(message);
|
||||||
preamble = false;
|
configured = false;
|
||||||
bit_pos = 0;
|
return;
|
||||||
} else {
|
}
|
||||||
cur_bit = (preamble_parts << bit_pos) >> 15;
|
} else {*/
|
||||||
bit_pos++;
|
|
||||||
}
|
for (size_t i = 0; i < buffer.count; i++) {
|
||||||
}
|
|
||||||
|
/*if (active) {
|
||||||
if (!preamble) {
|
if (!sample) {
|
||||||
|
sample = 300;
|
||||||
if (bit_pos >= 112) {
|
if (bit_pos >= 112) {
|
||||||
active = false; // Stop
|
active = false; // Stop
|
||||||
cur_bit = 0;
|
cur_bit = 0;
|
||||||
@ -59,38 +62,39 @@ void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
|||||||
cur_bit = shared_memory.bb_data.data[bit_pos];
|
cur_bit = shared_memory.bb_data.data[bit_pos];
|
||||||
bit_pos++;
|
bit_pos++;
|
||||||
}
|
}
|
||||||
|
} else
|
||||||
|
sample--;
|
||||||
|
|
||||||
|
if (!preamble) {
|
||||||
|
if (sample == 150)
|
||||||
|
cur_bit ^= 1; // Invert
|
||||||
}
|
}
|
||||||
} else
|
} else {*/
|
||||||
sample--;
|
/*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)
|
if (cur_bit) {
|
||||||
cur_bit ^= 1; // Invert
|
// Crude AM
|
||||||
} else {
|
buffer.p[i] = am_lut[phase & 3];
|
||||||
//cur_bit = 0;
|
phase++;
|
||||||
if (bit_pos == 8192) { // ?
|
} else {
|
||||||
configured = false;
|
buffer.p[i] = { 0, 0 };
|
||||||
message.done = true;
|
|
||||||
shared_memory.application_queue.push(message);
|
|
||||||
}
|
}
|
||||||
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) {
|
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) {
|
if (message.id == Message::ID::ADSBConfigure) {
|
||||||
bit_pos = 0;
|
bit_pos = 0;
|
||||||
sample = 0;
|
phase = 0;
|
||||||
cur_bit = 0;
|
|
||||||
preamble = true;
|
|
||||||
active = true;
|
active = true;
|
||||||
configured = true;
|
configured = true;
|
||||||
|
terminate = 0;
|
||||||
fm_delta = 50000 * (0xFFFFFFULL / 4000000); // Fixed bw for now
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,8 +26,6 @@
|
|||||||
#include "baseband_processor.hpp"
|
#include "baseband_processor.hpp"
|
||||||
#include "baseband_thread.hpp"
|
#include "baseband_thread.hpp"
|
||||||
|
|
||||||
#define TEST_F2D(f) (uint32_t)((f) * ((1ULL << 32) / 4000000))
|
|
||||||
|
|
||||||
class ADSBTXProcessor : public BasebandProcessor {
|
class ADSBTXProcessor : public BasebandProcessor {
|
||||||
public:
|
public:
|
||||||
void execute(const buffer_c8_t& buffer) override;
|
void execute(const buffer_c8_t& buffer) override;
|
||||||
@ -39,16 +37,18 @@ private:
|
|||||||
|
|
||||||
BasebandThread baseband_thread { 4000000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
BasebandThread baseband_thread { 4000000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||||
|
|
||||||
const uint16_t preamble_parts = 0b1010000101000000;
|
const complex8_t am_lut[4] = {
|
||||||
bool preamble { }, active { };
|
{ 127, 0 },
|
||||||
uint16_t bit_pos { 0 };
|
{ 0, 127 },
|
||||||
uint8_t cur_bit { 0 };
|
{ -127, 0 },
|
||||||
uint32_t sample { 0 };
|
{ 0, -127 }
|
||||||
uint32_t tone_phase { 0 };
|
};
|
||||||
uint32_t fm_delta { 0 };
|
|
||||||
uint32_t phase { 0 }, sphase { 0 };
|
bool active { };
|
||||||
int32_t tone_sample { 0 }, delta { 0 };
|
uint32_t terminate { };
|
||||||
int8_t re { }, im { };
|
uint32_t bit_pos { 0 };
|
||||||
|
uint32_t cur_bit { 0 };
|
||||||
|
uint32_t phase { 0 };
|
||||||
|
|
||||||
TXDoneMessage message { };
|
TXDoneMessage message { };
|
||||||
};
|
};
|
||||||
|
@ -129,7 +129,7 @@ typedef enum IRQn {
|
|||||||
|
|
||||||
#define __SIMD32_TYPE int32_t
|
#define __SIMD32_TYPE int32_t
|
||||||
#define __SIMD32(addr) (*(__SIMD32_TYPE **) & (addr))
|
#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
|
/* Overload of __SXTB16() to add ROR argument, since using __ROR() as an
|
||||||
* argument to the existing __SXTB16() doesn't produce optimum/sane code.
|
* 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 "ert_packet.hpp"
|
||||||
#include "tpms_packet.hpp"
|
#include "tpms_packet.hpp"
|
||||||
#include "pocsag_packet.hpp"
|
#include "pocsag_packet.hpp"
|
||||||
|
#include "adsb_frame.hpp"
|
||||||
#include "jammer.hpp"
|
#include "jammer.hpp"
|
||||||
#include "dsp_fir_taps.hpp"
|
#include "dsp_fir_taps.hpp"
|
||||||
#include "dsp_iir.hpp"
|
#include "dsp_iir.hpp"
|
||||||
@ -91,6 +92,7 @@ public:
|
|||||||
SigGenTone = 44,
|
SigGenTone = 44,
|
||||||
|
|
||||||
POCSAGPacket = 50,
|
POCSAGPacket = 50,
|
||||||
|
ADSBFrame = 51,
|
||||||
|
|
||||||
RequestSignal = 52,
|
RequestSignal = 52,
|
||||||
FIFOData = 53,
|
FIFOData = 53,
|
||||||
@ -317,6 +319,18 @@ public:
|
|||||||
pocsag::POCSAGPacket packet;
|
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 {
|
class ShutdownMessage : public Message {
|
||||||
public:
|
public:
|
||||||
constexpr ShutdownMessage(
|
constexpr ShutdownMessage(
|
||||||
|
@ -26,8 +26,6 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include "optional.hpp"
|
|
||||||
|
|
||||||
#include "baseband.hpp"
|
#include "baseband.hpp"
|
||||||
|
|
||||||
namespace pocsag {
|
namespace pocsag {
|
||||||
|
@ -63,6 +63,7 @@ private:
|
|||||||
char c[4];
|
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_ais { 'P', 'A', 'I', 'S' };
|
||||||
constexpr image_tag_t image_tag_am_audio { 'P', 'A', 'M', 'A' };
|
constexpr image_tag_t image_tag_am_audio { 'P', 'A', 'M', 'A' };
|
||||||
constexpr image_tag_t image_tag_capture { 'P', 'C', 'A', 'P' };
|
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_tones { 'P', 'T', 'O', 'N' };
|
||||||
constexpr image_tag_t image_tag_rds { 'P', 'R', 'D', 'S' };
|
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_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_replay { 'P', 'R', 'E', 'P' };
|
||||||
constexpr image_tag_t image_tag_fsktx { 'P', 'F', 'S', 'K' };
|
constexpr image_tag_t image_tag_fsktx { 'P', 'F', 'S', 'K' };
|
||||||
constexpr image_tag_t image_tag_mic_tx { 'P', 'M', 'T', 'X' };
|
constexpr image_tag_t image_tag_mic_tx { 'P', 'M', 'T', 'X' };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user