mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-10-01 01:26:06 -04:00
Restoring jammer and RDS functionalities, please wait...
Started work on frequency manager and numbers station simulator
This commit is contained in:
parent
ef0feae62b
commit
d18b6d135d
@ -135,7 +135,9 @@ set(CPPSRC
|
||||
touch_adc.cpp
|
||||
encoder.cpp
|
||||
audio.cpp
|
||||
adsb.cpp
|
||||
afsk.cpp
|
||||
rds.cpp
|
||||
${COMMON}/lcd_ili9341.cpp
|
||||
${COMMON}/ui.cpp
|
||||
${COMMON}/ui_text.cpp
|
||||
@ -170,7 +172,8 @@ set(CPPSRC
|
||||
ui_rds.cpp
|
||||
ui_lcr.cpp
|
||||
ui_xylos.cpp
|
||||
# ui_freqman.cpp
|
||||
ui_numbers.cpp
|
||||
ui_freqman.cpp
|
||||
ui_encoders.cpp
|
||||
ui_jammer.cpp
|
||||
# ui_loadmodule.cpp
|
||||
|
@ -2478,6 +2478,30 @@ __/common/wm8731.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/__/common/wm8731.cpp.s
|
||||
.PHONY : __/common/wm8731.cpp.s
|
||||
|
||||
adsb.obj: adsb.cpp.obj
|
||||
.PHONY : adsb.obj
|
||||
|
||||
# target to build an object file
|
||||
adsb.cpp.obj:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/adsb.cpp.obj
|
||||
.PHONY : adsb.cpp.obj
|
||||
|
||||
adsb.i: adsb.cpp.i
|
||||
.PHONY : adsb.i
|
||||
|
||||
# target to preprocess a source file
|
||||
adsb.cpp.i:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/adsb.cpp.i
|
||||
.PHONY : adsb.cpp.i
|
||||
|
||||
adsb.s: adsb.cpp.s
|
||||
.PHONY : adsb.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
adsb.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/adsb.cpp.s
|
||||
.PHONY : adsb.cpp.s
|
||||
|
||||
afsk.obj: afsk.cpp.obj
|
||||
.PHONY : afsk.obj
|
||||
|
||||
@ -3150,6 +3174,30 @@ radio.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/radio.cpp.s
|
||||
.PHONY : radio.cpp.s
|
||||
|
||||
rds.obj: rds.cpp.obj
|
||||
.PHONY : rds.obj
|
||||
|
||||
# target to build an object file
|
||||
rds.cpp.obj:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/rds.cpp.obj
|
||||
.PHONY : rds.cpp.obj
|
||||
|
||||
rds.i: rds.cpp.i
|
||||
.PHONY : rds.i
|
||||
|
||||
# target to preprocess a source file
|
||||
rds.cpp.i:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/rds.cpp.i
|
||||
.PHONY : rds.cpp.i
|
||||
|
||||
rds.s: rds.cpp.s
|
||||
.PHONY : rds.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
rds.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/rds.cpp.s
|
||||
.PHONY : rds.cpp.s
|
||||
|
||||
receiver_model.obj: receiver_model.cpp.obj
|
||||
.PHONY : receiver_model.obj
|
||||
|
||||
@ -3798,6 +3846,30 @@ ui_font_fixed_8x16.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_font_fixed_8x16.cpp.s
|
||||
.PHONY : ui_font_fixed_8x16.cpp.s
|
||||
|
||||
ui_freqman.obj: ui_freqman.cpp.obj
|
||||
.PHONY : ui_freqman.obj
|
||||
|
||||
# target to build an object file
|
||||
ui_freqman.cpp.obj:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_freqman.cpp.obj
|
||||
.PHONY : ui_freqman.cpp.obj
|
||||
|
||||
ui_freqman.i: ui_freqman.cpp.i
|
||||
.PHONY : ui_freqman.i
|
||||
|
||||
# target to preprocess a source file
|
||||
ui_freqman.cpp.i:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_freqman.cpp.i
|
||||
.PHONY : ui_freqman.cpp.i
|
||||
|
||||
ui_freqman.s: ui_freqman.cpp.s
|
||||
.PHONY : ui_freqman.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
ui_freqman.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_freqman.cpp.s
|
||||
.PHONY : ui_freqman.cpp.s
|
||||
|
||||
ui_handwrite.obj: ui_handwrite.cpp.obj
|
||||
.PHONY : ui_handwrite.obj
|
||||
|
||||
@ -3918,6 +3990,30 @@ ui_navigation.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_navigation.cpp.s
|
||||
.PHONY : ui_navigation.cpp.s
|
||||
|
||||
ui_numbers.obj: ui_numbers.cpp.obj
|
||||
.PHONY : ui_numbers.obj
|
||||
|
||||
# target to build an object file
|
||||
ui_numbers.cpp.obj:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_numbers.cpp.obj
|
||||
.PHONY : ui_numbers.cpp.obj
|
||||
|
||||
ui_numbers.i: ui_numbers.cpp.i
|
||||
.PHONY : ui_numbers.i
|
||||
|
||||
# target to preprocess a source file
|
||||
ui_numbers.cpp.i:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_numbers.cpp.i
|
||||
.PHONY : ui_numbers.cpp.i
|
||||
|
||||
ui_numbers.s: ui_numbers.cpp.s
|
||||
.PHONY : ui_numbers.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
ui_numbers.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_numbers.cpp.s
|
||||
.PHONY : ui_numbers.cpp.s
|
||||
|
||||
ui_nuoptix.obj: ui_nuoptix.cpp.obj
|
||||
.PHONY : ui_nuoptix.obj
|
||||
|
||||
@ -4534,6 +4630,9 @@ help:
|
||||
@echo "... __/common/wm8731.obj"
|
||||
@echo "... __/common/wm8731.i"
|
||||
@echo "... __/common/wm8731.s"
|
||||
@echo "... adsb.obj"
|
||||
@echo "... adsb.i"
|
||||
@echo "... adsb.s"
|
||||
@echo "... afsk.obj"
|
||||
@echo "... afsk.i"
|
||||
@echo "... afsk.s"
|
||||
@ -4618,6 +4717,9 @@ help:
|
||||
@echo "... radio.obj"
|
||||
@echo "... radio.i"
|
||||
@echo "... radio.s"
|
||||
@echo "... rds.obj"
|
||||
@echo "... rds.i"
|
||||
@echo "... rds.s"
|
||||
@echo "... receiver_model.obj"
|
||||
@echo "... receiver_model.i"
|
||||
@echo "... receiver_model.s"
|
||||
@ -4699,6 +4801,9 @@ help:
|
||||
@echo "... ui_font_fixed_8x16.obj"
|
||||
@echo "... ui_font_fixed_8x16.i"
|
||||
@echo "... ui_font_fixed_8x16.s"
|
||||
@echo "... ui_freqman.obj"
|
||||
@echo "... ui_freqman.i"
|
||||
@echo "... ui_freqman.s"
|
||||
@echo "... ui_handwrite.obj"
|
||||
@echo "... ui_handwrite.i"
|
||||
@echo "... ui_handwrite.s"
|
||||
@ -4714,6 +4819,9 @@ help:
|
||||
@echo "... ui_navigation.obj"
|
||||
@echo "... ui_navigation.i"
|
||||
@echo "... ui_navigation.s"
|
||||
@echo "... ui_numbers.obj"
|
||||
@echo "... ui_numbers.i"
|
||||
@echo "... ui_numbers.s"
|
||||
@echo "... ui_nuoptix.obj"
|
||||
@echo "... ui_nuoptix.i"
|
||||
@echo "... ui_nuoptix.s"
|
||||
|
135
firmware/application/adsb.cpp
Normal file
135
firmware/application/adsb.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
void make_frame_mode_s(uint8_t * adsb_frame, uint32_t ICAO_address) {
|
||||
adsb_frame[0] = 0x8D; // DF and CA
|
||||
adsb_frame[1] = ICAO_address >> 16;
|
||||
adsb_frame[2] = (ICAO_address >> 8) & 0xFF;
|
||||
adsb_frame[3] = ICAO_address & 0xFF;
|
||||
}
|
||||
|
||||
void generate_frame_id(uint8_t * adsb_frame, uint32_t ICAO_address, char * callsign) {
|
||||
uint8_t c, s;
|
||||
char ch;
|
||||
std::string callsign_formatted(8, '_');
|
||||
uint64_t callsign_coded = 0;
|
||||
|
||||
make_frame_mode_s(adsb_frame, ICAO_address);
|
||||
|
||||
adsb_frame[4] = 0x20; // TC
|
||||
|
||||
// Translate and code 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 = icao_id_lut[s];
|
||||
} else {
|
||||
ch = ' ';
|
||||
s = 32;
|
||||
}
|
||||
callsign_coded |= ((uint64_t)s << ((7 - c) * 6));
|
||||
callsign_formatted[c] = ch;
|
||||
}
|
||||
|
||||
// Insert callsign in frame
|
||||
for (c = 0; c < 6; c++)
|
||||
adsb_frame[c + 5] = (callsign_coded >> ((5 - c) * 8)) & 0xFF;
|
||||
|
||||
ADSB_generate_CRC(adsb_frame);
|
||||
}
|
||||
|
||||
void generate_frame_pos(uint8_t * adsb_frame, uint32_t ICAO_address, uint32_t altitude, float latitude, 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] = 0x58; // TC, SS and NICsb
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
void ADSB_generate_CRC(uint8_t * in_frame) {
|
||||
uint8_t adsb_crc[14]; // Temp buffer
|
||||
uint8_t b, c, s, bitn;
|
||||
const uint32_t crc_poly = 0x1205FFF;
|
||||
|
||||
in_frame[11] = 0x00; // Clear CRC
|
||||
in_frame[12] = 0x00;
|
||||
in_frame[13] = 0x00;
|
||||
|
||||
// Compute CRC
|
||||
memcpy(adsb_crc, in_frame, 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
|
||||
for (c = 0; c < 3; c++)
|
||||
in_frame[c + 11] = adsb_crc[c + 11];
|
||||
|
||||
}
|
||||
|
||||
} /* namespace adsb */
|
42
firmware/application/adsb.hpp
Normal file
42
firmware/application/adsb.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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__
|
||||
|
||||
namespace adsb {
|
||||
|
||||
const char icao_id_lut[65] = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######";
|
||||
|
||||
void make_frame_mode_s(uint8_t * adsb_frame, uint32_t ICAO_address);
|
||||
|
||||
void generate_frame_id(uint8_t * adsb_frame, uint32_t ICAO_address, char * callsign);
|
||||
void generate_frame_pos(uint8_t * adsb_frame, uint32_t ICAO_address, uint32_t altitude, float latitude, float longitude);
|
||||
|
||||
void ADSB_generate_CRC(uint8_t * in_message);
|
||||
|
||||
} /* namespace adsb */
|
||||
|
||||
#endif/*__ADSB_H__*/
|
@ -151,6 +151,13 @@ void set_adsb() {
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void set_rds_data(const uint16_t message_length) {
|
||||
const RDSConfigureMessage message {
|
||||
message_length
|
||||
};
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void set_dtmf_data(const uint32_t bw, const uint32_t tone_length, const uint32_t pause_length) {
|
||||
const DTMFTXConfigMessage message {
|
||||
bw,
|
||||
|
@ -63,6 +63,7 @@ void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit,
|
||||
const uint32_t pause_symbols);
|
||||
void set_pocsag();
|
||||
void set_adsb();
|
||||
void set_rds_data(const uint16_t message_length);
|
||||
void set_dtmf_data(const uint32_t bw, const uint32_t tone_length, const uint32_t pause_length);
|
||||
|
||||
void run_image(const portapack::spi_flash::image_tag_t image_tag);
|
||||
|
@ -23,9 +23,14 @@
|
||||
// Bitmaps generated with:
|
||||
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
|
||||
|
||||
//TEST: Numbers
|
||||
//TEST: Jammer
|
||||
//TEST: RDS
|
||||
|
||||
//BUG: Unistroke text entry screen doesn't care about string max length parameter
|
||||
//BUG (fixed ?): No audio in about when shown second time
|
||||
//BUG: POCSAG RX sometimes misses the first codeword after SYNC
|
||||
//BUG: Soundboard crashes on exit if no wav files on sd card
|
||||
|
||||
//TODO: Use ModalMessageView with yes/no for TX
|
||||
//TODO: Show address/data bit fields in OOK TX
|
||||
@ -33,7 +38,6 @@
|
||||
//TODO: Check more OOK encoders
|
||||
//TODO: POCSAG 512 and 2400 (all 3 at the same time, or parameter ?)
|
||||
//TODO: Check AFSK transmit end, skips last bits ?
|
||||
//TODO: Check jammer bandwidths
|
||||
//TODO: Use msgpack for settings, lists... on sd card
|
||||
//TODO: Frequency manager
|
||||
//TODO: Morse coder
|
||||
|
175
firmware/application/rds.cpp
Normal file
175
firmware/application/rds.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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 "rds.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
namespace rds {
|
||||
|
||||
uint32_t makeblock(uint32_t blockdata, uint16_t offset) {
|
||||
uint16_t CRC = 0;
|
||||
uint8_t doinv;
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
doinv = (((blockdata << i) & 0x8000) >> 15) ^ (CRC >> 9);
|
||||
if (doinv) CRC ^= 0b0011011100;
|
||||
CRC = ((CRC << 1) | doinv) & 0x3FF;
|
||||
}
|
||||
|
||||
return (blockdata << 10) | (CRC ^ offset);
|
||||
}
|
||||
|
||||
// Todo:
|
||||
// Make PI
|
||||
// TA/TP flags
|
||||
// Group selection
|
||||
|
||||
// Boolean to binary
|
||||
uint8_t b2b(const bool in) {
|
||||
if (in)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void make_0B_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA,
|
||||
const bool MS, const bool DI, const uint8_t C, const char * chars) {
|
||||
|
||||
group[0] = PI_code;
|
||||
group[1] = (0x0 << 12) | (1 << 11) | (b2b(TP) << 10) | (PTY << 5) | (b2b(TA) << 4) | (b2b(MS) << 3) | (b2b(DI) << 2) | (C & 3);
|
||||
group[2] = PI_code;
|
||||
group[3] = (chars[0] << 8) | chars[1];
|
||||
}
|
||||
|
||||
void make_2A_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB,
|
||||
const uint8_t segment, const char * chars) {
|
||||
|
||||
group[0] = PI_code;
|
||||
group[1] = (0x0 << 12) | (1 << 11) | (b2b(TP) << 10) | (PTY << 5) | (b2b(AB) << 4) | (segment & 15);
|
||||
group[2] = (chars[0] << 8) | chars[1];
|
||||
group[3] = (chars[2] << 8) | chars[3];
|
||||
}
|
||||
|
||||
uint16_t gen_PSN(const char * psname, const uint8_t pty) {
|
||||
uint8_t c;
|
||||
uint32_t group[4][4] = { 0 };
|
||||
|
||||
// 4 groups with 2 PSN characters in each
|
||||
make_0B_group(&group[0][0], 0xF849, true, pty, false, true, false, 0, &psname[0]);
|
||||
make_0B_group(&group[1][0], 0xF849, true, pty, false, true, false, 1, &psname[2]);
|
||||
make_0B_group(&group[2][0], 0xF849, true, pty, false, true, false, 2, &psname[4]);
|
||||
make_0B_group(&group[3][0], 0xF849, true, pty, false, true, false, 3, &psname[6]);
|
||||
|
||||
/*uint32_t group[4][4] = {
|
||||
{
|
||||
0b1111100001001001, //PI
|
||||
0b0000110011101000, //Address
|
||||
0b1111100001001001, //PI
|
||||
0b0000000000000000 //Replaced
|
||||
},
|
||||
|
||||
{
|
||||
0b1111100001001001, //PI
|
||||
0b0000110011101001, //Address
|
||||
0b1111100001001001, //PI
|
||||
0b0000000000000000 //Replaced
|
||||
},
|
||||
|
||||
{
|
||||
0b1111100001001001, //PI
|
||||
0b0000110011101010, //Address
|
||||
0b1111100001001001, //PI
|
||||
0b0000000000000000 //Replaced
|
||||
},
|
||||
|
||||
{
|
||||
0b1111100001001001, //PI
|
||||
0b0000110011101011, //Address
|
||||
0b1111100001001001, //PI
|
||||
0b0000000000000000 //Replaced
|
||||
},
|
||||
};
|
||||
|
||||
//Insert PSN data in groups
|
||||
group[0][3] = (psname[0] << 8) | psname[1];
|
||||
group[1][3] = (psname[2] << 8) | psname[3];
|
||||
group[2][3] = (psname[4] << 8) | psname[5];
|
||||
group[3][3] = (psname[6] << 8) | psname[7];
|
||||
*/
|
||||
|
||||
// Generate checkbits for all blocks
|
||||
for (c = 0; c < 4; c++) {
|
||||
group[c][0] = makeblock(group[c][0], RDS_OFFSET_A);
|
||||
group[c][1] = makeblock(group[c][1], RDS_OFFSET_B);
|
||||
group[c][2] = makeblock(group[c][2], RDS_OFFSET_Cp); // C' !
|
||||
group[c][3] = makeblock(group[c][3], RDS_OFFSET_D);
|
||||
}
|
||||
|
||||
// Todo
|
||||
//for (c = 0; c < 16; c++)
|
||||
// shared_memory.radio_data[c] = group[c >> 2][c & 3];
|
||||
|
||||
return 4 * 4 * 26;
|
||||
}
|
||||
|
||||
uint16_t gen_RadioText(const char * radiotext, const uint8_t pty) {
|
||||
size_t c, i;
|
||||
uint32_t * group;
|
||||
char radiotext_buffer[65] = { 0 };
|
||||
uint8_t rtlen, groups;
|
||||
|
||||
strcpy(radiotext_buffer, radiotext);
|
||||
|
||||
rtlen = strlen(radiotext_buffer);
|
||||
|
||||
radiotext_buffer[rtlen] = 0x0D;
|
||||
|
||||
// Pad to multiple of 4
|
||||
while (rtlen & 3)
|
||||
radiotext_buffer[rtlen++] = ' ';
|
||||
|
||||
groups = rtlen >> 2; // 4 characters per group
|
||||
|
||||
group = (uint32_t*)chHeapAlloc(0x0, 4 * groups * sizeof(uint32_t));
|
||||
|
||||
for (c = 0; c < groups; c++)
|
||||
make_2A_group(&group[c << 2], 0xF849, true, pty, false, c, &radiotext_buffer[c * 4]);
|
||||
|
||||
// Generate checkbits
|
||||
for (c = 0; c < groups; c++) {
|
||||
i = c * 4;
|
||||
group[i + 0] = makeblock(group[i + 0], RDS_OFFSET_A);
|
||||
group[i + 1] = makeblock(group[i + 1], RDS_OFFSET_B);
|
||||
group[i + 2] = makeblock(group[i + 2], RDS_OFFSET_C);
|
||||
group[i + 3] = makeblock(group[i + 3], RDS_OFFSET_D);
|
||||
}
|
||||
|
||||
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.tx_data;
|
||||
|
||||
for (c = 0; c < (groups * 4); c++)
|
||||
tx_data_u32[c] = group[c];
|
||||
|
||||
return groups * 4 * 26;
|
||||
}
|
||||
|
||||
} /* namespace rds */
|
49
firmware/application/rds.hpp
Normal file
49
firmware/application/rds.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "ch.h"
|
||||
|
||||
#ifndef __RDS_H__
|
||||
#define __RDS_H__
|
||||
|
||||
namespace rds {
|
||||
|
||||
#define RDS_OFFSET_A 0b0011111100
|
||||
#define RDS_OFFSET_B 0b0110011000
|
||||
#define RDS_OFFSET_C 0b0101101000
|
||||
#define RDS_OFFSET_Cp 0b1101010000
|
||||
#define RDS_OFFSET_D 0b0110110100
|
||||
|
||||
uint32_t makeblock(uint32_t blockdata, uint16_t offset);
|
||||
uint8_t b2b(const bool in);
|
||||
void make_0B_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA,
|
||||
const bool MS, const bool DI, const uint8_t C, const char * chars);
|
||||
void make_2A_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB,
|
||||
const uint8_t segment, const char * chars);
|
||||
uint16_t gen_PSN(const char * psname, const uint8_t pty);
|
||||
uint16_t gen_RadioText(const char * radiotext, const uint8_t pty);
|
||||
|
||||
} /* namespace rds */
|
||||
|
||||
#endif/*__RDS_H__*/
|
@ -55,6 +55,21 @@ static char* to_string_dec_uint_pad_internal(
|
||||
return q;
|
||||
}
|
||||
|
||||
std::string to_string_bin(
|
||||
const uint32_t n,
|
||||
const uint8_t l)
|
||||
{
|
||||
char p[33];
|
||||
for (uint8_t c = 0; c < l; c++) {
|
||||
if (n & (1 << (l - c)))
|
||||
p[c] = '1';
|
||||
else
|
||||
p[c] = '0';
|
||||
}
|
||||
p[l] = 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
std::string to_string_dec_uint(
|
||||
const uint32_t n,
|
||||
const int32_t l,
|
||||
|
@ -30,6 +30,7 @@
|
||||
using namespace lpc43xx;
|
||||
|
||||
// TODO: Allow l=0 to not fill/justify? Already using this way in ui_spectrum.hpp...
|
||||
std::string to_string_bin(const uint32_t n, const uint8_t l = 0);
|
||||
std::string to_string_dec_uint(const uint32_t n, const int32_t l = 0, const char fill = 0);
|
||||
std::string to_string_dec_int(const int32_t n, const int32_t l = 0, const char fill = 0);
|
||||
std::string to_string_hex(const uint64_t n, const int32_t l = 0);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "ui_adsbtx.hpp"
|
||||
#include "ui_alphanum.hpp"
|
||||
|
||||
#include "adsb.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
@ -31,6 +32,7 @@
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace adsb;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
@ -45,149 +47,27 @@ ADSBTxView::~ADSBTxView() {
|
||||
}
|
||||
|
||||
void ADSBTxView::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
button_callsign.set_text(callsign);
|
||||
}
|
||||
|
||||
void ADSBTxView::generate_frame_pos() {
|
||||
uint8_t b, c, s, time_parity, bitn;
|
||||
char ch;
|
||||
void ADSBTxView::generate_frame() {
|
||||
uint8_t c;
|
||||
std::string str_debug;
|
||||
uint16_t altitude_coded = (38000 + 1000) / 25;
|
||||
uint32_t LAT, LON;
|
||||
float delta_lat, yz, rlat, delta_lon, xz;
|
||||
uint8_t adsb_crc[14]; // Temp buffer
|
||||
uint32_t crc_poly = 0x1205FFF;
|
||||
|
||||
LAT = 0;
|
||||
if (options_format.selected_index() == 2)
|
||||
button_transmit.hidden(true);
|
||||
|
||||
adsb_frame[0] = (options_format.selected_index_value() << 3) | 5; // DF and CA
|
||||
adsb_frame[1] = 0x48; // ICAO24
|
||||
adsb_frame[2] = 0x40;
|
||||
adsb_frame[3] = 0xD6;
|
||||
adsb_frame[4] = 0x58; // TC, SS and NICsb
|
||||
generate_frame_id(adsb_frame, sym_icao.value_hex_u64(), callsign);
|
||||
|
||||
// altitude = (feet + 1000) / 25
|
||||
memset(adsb_bin, 0, 112);
|
||||
|
||||
// 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
|
||||
|
||||
adsb_frame[11] = 0x00; // Clear CRC
|
||||
adsb_frame[12] = 0x00;
|
||||
adsb_frame[13] = 0x00;
|
||||
|
||||
// Compute CRC
|
||||
memcpy(adsb_crc, adsb_frame, 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));
|
||||
}
|
||||
}
|
||||
// Convert to binary (1 bit per byte, faster for baseband code)
|
||||
for (c = 0; c < 112; c++) {
|
||||
if ((adsb_frame[c >> 3] << (c & 7)) & 0x80) {
|
||||
adsb_bin[c] = 0xFF;
|
||||
}
|
||||
}
|
||||
// Insert CRC in frame
|
||||
for (c = 0; c < 3; c++)
|
||||
adsb_frame[c + 11] = adsb_crc[c + 11];
|
||||
|
||||
// Convert to binary
|
||||
for (c = 0; c < 112; c++)
|
||||
adsb_bin[c] = (adsb_frame[c >> 3] >> (7 - (c & 7))) & 1;
|
||||
|
||||
// Display for debug
|
||||
str_debug = "";
|
||||
for (c = 0; c < 7; c++)
|
||||
str_debug += to_string_hex(adsb_frame[c], 2);
|
||||
text_frame_a.set(str_debug);
|
||||
str_debug = "";
|
||||
for (c = 0; c < 7; c++)
|
||||
str_debug += to_string_hex(adsb_frame[c + 7], 2);
|
||||
text_frame_b.set(str_debug);
|
||||
}
|
||||
|
||||
void ADSBTxView::generate_frame_id() {
|
||||
uint8_t b, c, s, bitn;
|
||||
char ch;
|
||||
std::string str_debug;
|
||||
std::string callsign_formatted(8, '_');
|
||||
uint64_t callsign_coded = 0;
|
||||
uint8_t adsb_crc[14]; // Temp buffer
|
||||
uint32_t crc_poly = 0x1205FFF;
|
||||
|
||||
// Init frame
|
||||
//memset(adsb_frame, 0, 120);
|
||||
|
||||
adsb_frame[0] = (options_format.selected_index_value() << 3) | 5; // DF and CA
|
||||
adsb_frame[1] = 0x48; // ICAO24
|
||||
adsb_frame[2] = 0x40;
|
||||
adsb_frame[3] = 0xD6;
|
||||
adsb_frame[4] = 0x20; // TC
|
||||
|
||||
adsb_frame[11] = 0x00; // Clear CRC
|
||||
adsb_frame[12] = 0x00;
|
||||
adsb_frame[13] = 0x00;
|
||||
|
||||
// Translate and code 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 = icao_id_lut[s];
|
||||
} else {
|
||||
ch = ' ';
|
||||
s = 32;
|
||||
}
|
||||
callsign_coded |= ((uint64_t)s << ((7 - c) * 6));
|
||||
callsign_formatted[c] = ch;
|
||||
}
|
||||
|
||||
// Insert callsign in frame
|
||||
for (c = 0; c < 6; c++)
|
||||
adsb_frame[c + 5] = (callsign_coded >> ((5 - c) * 8)) & 0xFF;
|
||||
|
||||
// Compute CRC
|
||||
memcpy(adsb_crc, adsb_frame, 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
|
||||
for (c = 0; c < 3; c++)
|
||||
adsb_frame[c + 11] = adsb_crc[c + 11];
|
||||
|
||||
// Convert to binary
|
||||
for (c = 0; c < 112; c++)
|
||||
adsb_bin[c] = (adsb_frame[c >> 3] >> (7 - (c & 7))) & 1;
|
||||
|
||||
// Display for debug
|
||||
str_debug = "";
|
||||
@ -199,7 +79,7 @@ void ADSBTxView::generate_frame_id() {
|
||||
str_debug += to_string_hex(adsb_frame[c + 7], 2);
|
||||
text_frame_b.set(str_debug);
|
||||
|
||||
text_message.set(callsign_formatted);
|
||||
//text_message.set(callsign_formatted);
|
||||
}
|
||||
|
||||
void ADSBTxView::start_tx() {
|
||||
@ -224,13 +104,13 @@ void ADSBTxView::on_txdone(const int n) {
|
||||
|
||||
if (n == 200) {
|
||||
transmitter_model.disable();
|
||||
progress.set_value(0);
|
||||
//progress.set_value(0);
|
||||
|
||||
tx_mode = IDLE;
|
||||
button_transmit.set_style(&style_val);
|
||||
button_transmit.set_text("START");
|
||||
} else {
|
||||
progress.set_value(n);
|
||||
//progress.set_value(n);
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,32 +125,49 @@ ADSBTxView::ADSBTxView(NavigationView& nav) {
|
||||
&text_format,
|
||||
&options_format,
|
||||
&text_icaolabel,
|
||||
&button_icao,
|
||||
&sym_icao,
|
||||
&text_callsign,
|
||||
&button_callsign,
|
||||
&text_altitude,
|
||||
&field_altitude,
|
||||
&text_latitude,
|
||||
&field_lat_degrees,
|
||||
&field_lat_minutes,
|
||||
&field_lat_seconds,
|
||||
&text_longitude,
|
||||
&field_lon_degrees,
|
||||
&field_lon_minutes,
|
||||
&field_lon_seconds,
|
||||
&text_frame_a,
|
||||
&text_frame_b,
|
||||
&progress,
|
||||
&text_message,
|
||||
&button_transmit
|
||||
} });
|
||||
|
||||
options_format.set_by_value(17); // Mode S
|
||||
|
||||
progress.set_max(122);
|
||||
|
||||
options_format.on_change = [this](size_t i, int32_t v) {
|
||||
(void)i;
|
||||
(void)v;
|
||||
generate_frame_id();
|
||||
generate_frame();
|
||||
};
|
||||
sym_icao.on_change = [this]() {
|
||||
generate_frame();
|
||||
};
|
||||
button_callsign.on_select = [this, &nav](Button&) {
|
||||
textentry(nav, callsign, 9);
|
||||
};
|
||||
|
||||
field_altitude.set_value(11000);
|
||||
field_lat_degrees.set_value(0);
|
||||
field_lat_minutes.set_value(0);
|
||||
field_lat_seconds.set_value(0);
|
||||
field_lon_degrees.set_value(0);
|
||||
field_lon_minutes.set_value(0);
|
||||
field_lon_seconds.set_value(0);
|
||||
|
||||
button_transmit.set_style(&style_val);
|
||||
|
||||
generate_frame_id();
|
||||
generate_frame();
|
||||
|
||||
// Single transmit
|
||||
button_transmit.on_select = [this, &nav](Button&) {
|
||||
@ -278,7 +175,7 @@ ADSBTxView::ADSBTxView(NavigationView& nav) {
|
||||
tx_mode = SINGLE;
|
||||
button_transmit.set_style(&style_cancel);
|
||||
button_transmit.set_text("Wait");
|
||||
generate_frame_id();
|
||||
generate_frame();
|
||||
start_tx();
|
||||
}
|
||||
};
|
||||
|
@ -57,10 +57,8 @@ private:
|
||||
uint8_t adsb_frame[14]; // 112 bit data block as 14 bytes
|
||||
uint8_t adsb_bin[112]; // 112 bit data block
|
||||
|
||||
const char icao_id_lut[65] = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######";
|
||||
|
||||
void start_tx();
|
||||
void generate_frame_id();
|
||||
void generate_frame();
|
||||
void generate_frame_pos();
|
||||
void on_txdone(const int n);
|
||||
|
||||
@ -74,18 +72,13 @@ private:
|
||||
.background = Color::red(),
|
||||
.foreground = Color::black(),
|
||||
};
|
||||
const Style style_grey {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::grey(),
|
||||
};
|
||||
|
||||
Text text_format {
|
||||
{ 4 * 8, 1 * 16, 7 * 8, 16 },
|
||||
{ 2 * 8, 1 * 16, 7 * 8, 16 },
|
||||
"Format:"
|
||||
};
|
||||
OptionsField options_format {
|
||||
{ 12 * 8, 1 * 16 },
|
||||
{ 10 * 8, 1 * 16 },
|
||||
10,
|
||||
{
|
||||
{ "17: ADS-B", 17 },
|
||||
@ -95,40 +88,72 @@ private:
|
||||
};
|
||||
|
||||
Text text_icaolabel {
|
||||
{ 4 * 8, 3 * 16, 7 * 8, 16 },
|
||||
{ 2 * 8, 2 * 16, 7 * 8, 16 },
|
||||
"ICAO24:"
|
||||
};
|
||||
Button button_icao {
|
||||
{ 12 * 8, 2 * 16 + 12, 8 * 8, 24 },
|
||||
"012345" // 7277A9
|
||||
SymField sym_icao {
|
||||
{ 10 * 8, 2 * 16 },
|
||||
6,
|
||||
true // Hex
|
||||
};
|
||||
|
||||
Text text_callsign {
|
||||
{ 4 * 8, 4 * 16 + 8, 3 * 8, 16 },
|
||||
{ 2 * 8, 3 * 16 + 8, 3 * 8, 16 },
|
||||
"ID:"
|
||||
};
|
||||
Button button_callsign {
|
||||
{ 8 * 8, 4 * 16 + 4, 10 * 8, 24 },
|
||||
"" // "KOR151 "
|
||||
{ 6 * 8, 3 * 16 + 4, 10 * 8, 24 },
|
||||
""
|
||||
};
|
||||
|
||||
Text text_altitude {
|
||||
{ 2 * 8, 5 * 16, 20 * 8, 16 },
|
||||
"Altitude: feet"
|
||||
};
|
||||
NumberField field_altitude {
|
||||
{ 12 * 8, 5 * 16 },
|
||||
5,
|
||||
{ -1000, 50000 },
|
||||
250,
|
||||
' '
|
||||
};
|
||||
|
||||
Text text_latitude {
|
||||
{ 2 * 8, 6 * 16, 20 * 8, 16 },
|
||||
"Latitude: * ' \"" // No ° symbol in 8x16 font
|
||||
};
|
||||
NumberField field_lat_degrees {
|
||||
{ 12 * 8, 6 * 16 }, 3, { -90, 90 }, 1, ' '
|
||||
};
|
||||
NumberField field_lat_minutes {
|
||||
{ 16 * 8, 6 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
NumberField field_lat_seconds {
|
||||
{ 19 * 8, 6 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
Text text_longitude {
|
||||
{ 2 * 8, 7 * 16, 20 * 8, 16 },
|
||||
"Longitude: * ' \"" // No ° symbol in 8x16 font
|
||||
};
|
||||
NumberField field_lon_degrees {
|
||||
{ 12 * 8, 7 * 16 }, 3, { -90, 90 }, 1, ' '
|
||||
};
|
||||
NumberField field_lon_minutes {
|
||||
{ 16 * 8, 7 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
NumberField field_lon_seconds {
|
||||
{ 19 * 8, 7 * 16 }, 2, { 0, 59 }, 1, ' '
|
||||
};
|
||||
|
||||
Text text_frame_a {
|
||||
{ 4 * 8, 10 * 16, 14 * 8, 16 },
|
||||
{ 4 * 8, 12 * 16, 14 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
Text text_frame_b {
|
||||
{ 4 * 8, 11 * 16, 14 * 8, 16 },
|
||||
{ 4 * 8, 13 * 16, 14 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
ProgressBar progress {
|
||||
{ 5 * 8, 13 * 16, 20 * 8, 16 },
|
||||
};
|
||||
Text text_message {
|
||||
{ 5 * 8, 14 * 16, 20 * 8, 16 },
|
||||
"--------------------"
|
||||
};
|
||||
|
||||
Button button_transmit {
|
||||
{ 2 * 8, 16 * 16, 64, 32 },
|
||||
"START"
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -77,7 +78,7 @@ AlphanumView::AlphanumView(
|
||||
};
|
||||
|
||||
n = 0;
|
||||
for(auto& button : buttons) {
|
||||
for (auto& button : buttons) {
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({
|
||||
static_cast<Coord>((n % 5) * button_w),
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -54,7 +55,7 @@ private:
|
||||
bool _lowercase = false;
|
||||
static constexpr size_t button_w = 240 / 5;
|
||||
static constexpr size_t button_h = 28;
|
||||
char txtinput[25] = {0};
|
||||
char txtinput[29] = { 0 }; // 28 chars max
|
||||
|
||||
void char_add(const char c);
|
||||
void char_delete();
|
||||
|
@ -23,11 +23,9 @@
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#include "ff.h"
|
||||
#include "portapack.hpp"
|
||||
#include "event_m0.hpp"
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
@ -69,12 +67,4 @@ FreqManView::FreqManView(
|
||||
|
||||
}
|
||||
|
||||
void FreqManView::on_show() {
|
||||
|
||||
}
|
||||
|
||||
void FreqManView::on_hide() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,11 +32,12 @@ class FreqManView : public View {
|
||||
public:
|
||||
FreqManView(NavigationView& nav);
|
||||
//~FreqManView();
|
||||
|
||||
std::string title() const override { return "Frequency list"; };
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
//void on_show() override;
|
||||
//void on_hide() override;
|
||||
|
||||
private:
|
||||
std::array<Text, 10> text_list;
|
||||
|
@ -23,8 +23,6 @@
|
||||
#include "ui_jammer.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
|
||||
//#include "ch.h"
|
||||
//#include "evtimer.h"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
@ -47,8 +45,10 @@ JammerView::~JammerView() {
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void JammerView::updfreq(uint8_t id, rf::Frequency f) {
|
||||
void JammerView::update_text(uint8_t id, rf::Frequency f) {
|
||||
char finalstr[25] = {0};
|
||||
rf::Frequency center;
|
||||
std::string bw;
|
||||
uint8_t c;
|
||||
|
||||
auto mhz = to_string_dec_int(f / 1000000, 3);
|
||||
@ -59,64 +59,14 @@ void JammerView::updfreq(uint8_t id, rf::Frequency f) {
|
||||
strcat(finalstr, hz100.c_str());
|
||||
strcat(finalstr, "M");
|
||||
|
||||
while (strlen(finalstr) < 10) {
|
||||
while (strlen(finalstr) < 10)
|
||||
strcat(finalstr, " ");
|
||||
}
|
||||
|
||||
if (id == 0) {
|
||||
range1_min = f;
|
||||
this->button_setfreq1_min.set_text(finalstr);
|
||||
}
|
||||
if (id == 1) {
|
||||
range1_max = f;
|
||||
this->button_setfreq1_max.set_text(finalstr);
|
||||
}
|
||||
if (id == 2) {
|
||||
range2_min = f;
|
||||
this->button_setfreq2_min.set_text(finalstr);
|
||||
}
|
||||
if (id == 3) {
|
||||
range2_max = f;
|
||||
this->button_setfreq2_max.set_text(finalstr);
|
||||
}
|
||||
if (id == 4) {
|
||||
range3_min = f;
|
||||
this->button_setfreq3_min.set_text(finalstr);
|
||||
}
|
||||
if (id == 5) {
|
||||
range3_max = f;
|
||||
this->button_setfreq3_max.set_text(finalstr);
|
||||
}
|
||||
|
||||
rf::Frequency center;
|
||||
std::string bw;
|
||||
buttons_freq[id].set_text(finalstr);
|
||||
|
||||
for (c = 0; c < 3; c++) {
|
||||
if (c == 0) {
|
||||
center = (range1_min + range1_max) / 2;
|
||||
range1_center = center;
|
||||
}
|
||||
if (c == 1) {
|
||||
center = (range2_min + range2_max) / 2;
|
||||
range2_center = center;
|
||||
}
|
||||
if (c == 2) {
|
||||
center = (range3_min + range3_max) / 2;
|
||||
range3_center = center;
|
||||
}
|
||||
|
||||
if (c == 0) {
|
||||
range1_width = abs(range1_max - range1_min) / 1000;
|
||||
bw = to_string_dec_int(range1_width, 5);
|
||||
}
|
||||
if (c == 1) {
|
||||
range2_width = abs(range2_max - range2_min) / 1000;
|
||||
bw = to_string_dec_int(range2_width, 5);
|
||||
}
|
||||
if (c == 2) {
|
||||
range3_width = abs(range3_max - range3_min) / 1000;
|
||||
bw = to_string_dec_int(range3_width, 5);
|
||||
}
|
||||
center = (frequency_range[c].min + frequency_range[c].max) / 2;
|
||||
bw = to_string_dec_int(abs(frequency_range[c].max - frequency_range[c].min) / 1000, 5);
|
||||
|
||||
auto center_mhz = to_string_dec_int(center / 1000000, 4);
|
||||
auto center_hz100 = to_string_dec_int((center / 100) % 10000, 4, '0');
|
||||
@ -129,23 +79,23 @@ void JammerView::updfreq(uint8_t id, rf::Frequency f) {
|
||||
strcat(finalstr, bw.c_str());
|
||||
strcat(finalstr, "kHz");
|
||||
|
||||
while (strlen(finalstr) < 23) {
|
||||
while (strlen(finalstr) < 23)
|
||||
strcat(finalstr, " ");
|
||||
}
|
||||
|
||||
if (c == 0) this->text_info1.set(finalstr);
|
||||
if (c == 1) this->text_info2.set(finalstr);
|
||||
if (c == 2) this->text_info3.set(finalstr);
|
||||
if (c == 0) text_info1.set(finalstr);
|
||||
if (c == 1) text_info2.set(finalstr);
|
||||
if (c == 2) text_info3.set(finalstr);
|
||||
}
|
||||
}
|
||||
|
||||
void JammerView::on_retune(const int64_t freq) {
|
||||
if (freq > 0) {
|
||||
radio::set_tuning_frequency(freq);
|
||||
}
|
||||
if (freq > 0)
|
||||
transmitter_model.set_tuning_frequency(freq);
|
||||
}
|
||||
|
||||
JammerView::JammerView(NavigationView& nav) {
|
||||
size_t n;
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_jammer);
|
||||
|
||||
static constexpr Style style_val {
|
||||
@ -166,6 +116,8 @@ JammerView::JammerView(NavigationView& nav) {
|
||||
.foreground = Color::grey(),
|
||||
};
|
||||
|
||||
JammerRange * jammer_ranges = (JammerRange*)shared_memory.tx_data;
|
||||
|
||||
add_children({ {
|
||||
&text_type,
|
||||
&options_modulation,
|
||||
@ -178,158 +130,139 @@ JammerView::JammerView(NavigationView& nav) {
|
||||
&checkbox_range1,
|
||||
&checkbox_range2,
|
||||
&checkbox_range3,
|
||||
&button_setfreq1_min,
|
||||
&button_setfreq1_max,
|
||||
&text_info1,
|
||||
&button_setfreq2_min,
|
||||
&button_setfreq2_max,
|
||||
&text_info2,
|
||||
&button_setfreq3_min,
|
||||
&button_setfreq3_max,
|
||||
&text_info3,
|
||||
&button_transmit,
|
||||
&button_exit
|
||||
} });
|
||||
|
||||
const auto button_freq_fn = [this, &nav](Button& button) {
|
||||
uint16_t id = button.id;
|
||||
rf::Frequency * value_ptr;
|
||||
|
||||
if (id & 1)
|
||||
value_ptr = &frequency_range[id].max;
|
||||
else
|
||||
value_ptr = &frequency_range[id].min;
|
||||
auto new_view = nav.push<FrequencyKeypadView>(*value_ptr);
|
||||
new_view->on_changed = [this, value_ptr](rf::Frequency f) {
|
||||
*value_ptr = f;
|
||||
};
|
||||
};
|
||||
|
||||
n = 0;
|
||||
for (auto& button : buttons_freq) {
|
||||
button.on_select = button_freq_fn;
|
||||
button.set_parent_rect({
|
||||
static_cast<Coord>(13 * 8),
|
||||
static_cast<Coord>((n * 52) + 91 + (17 * (n & 1))),
|
||||
88, 18
|
||||
});
|
||||
button.id = n;
|
||||
button.set_text("----.----M");
|
||||
add_child(&button);
|
||||
n++;
|
||||
}
|
||||
|
||||
button_transmit.set_style(&style_val);
|
||||
text_info1.set_style(&style_info);
|
||||
text_info2.set_style(&style_info);
|
||||
text_info3.set_style(&style_info);
|
||||
|
||||
options_preset.set_selected_index(8);
|
||||
|
||||
options_preset.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
uint8_t c;
|
||||
|
||||
for (uint8_t c = 0; c < 3; c++) {
|
||||
frequency_range[c].min = range_presets[v][c].min;
|
||||
frequency_range[c].max = range_presets[v][c].max;
|
||||
}
|
||||
checkbox_range1.set_value(range_presets[v][0].enabled);
|
||||
checkbox_range2.set_value(range_presets[v][1].enabled);
|
||||
checkbox_range3.set_value(range_presets[v][2].enabled);
|
||||
};
|
||||
|
||||
options_preset.set_selected_index(8); // Sigfox, because they deserve it
|
||||
|
||||
button_transmit.on_select = [this, &nav, jammer_ranges](Button&) {
|
||||
uint8_t c, i = 0;
|
||||
size_t num_ranges;
|
||||
rf::Frequency start_freq, range_bw, ch_width;
|
||||
bool out_of_ranges = false;
|
||||
|
||||
// Disable all ranges by default
|
||||
for (i = 0; i < 9; i++)
|
||||
jammer_ranges[i].enabled = false;
|
||||
|
||||
// Generate jamming "channels", maximum: 9
|
||||
// Convert ranges min/max to center/bw
|
||||
for (c = 0; c < 3; c++) {
|
||||
updfreq(c*2, range_presets[v][c].min);
|
||||
updfreq((c*2)+1, range_presets[v][c].max);
|
||||
if (c == 0) checkbox_range1.set_value(range_presets[v][c].active);
|
||||
if (c == 1) checkbox_range2.set_value(range_presets[v][c].active);
|
||||
if (c == 2) checkbox_range3.set_value(range_presets[v][c].active);
|
||||
}
|
||||
};
|
||||
|
||||
button_setfreq1_min.on_select = [this,&nav](Button&){
|
||||
auto new_view = nav.push<FrequencyKeypadView>(range1_min);
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
updfreq(0, f);
|
||||
};
|
||||
};
|
||||
button_setfreq1_max.on_select = [this,&nav](Button&){
|
||||
auto new_view = nav.push<FrequencyKeypadView>(range1_max);
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
updfreq(0, f);
|
||||
};
|
||||
};
|
||||
|
||||
button_transmit.on_select = [this](Button&) {
|
||||
uint8_t i = 0;
|
||||
rf::Frequency t, range_lower;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
shared_memory.jammer_ranges[i].active = false;
|
||||
}
|
||||
|
||||
// Swap
|
||||
if (range1_min > range1_max) {
|
||||
t = range1_min;
|
||||
range1_min = range1_max;
|
||||
range1_max = t;
|
||||
}
|
||||
i = 0;
|
||||
range_lower = range1_min;
|
||||
// for (i = 0; i < 3; i++) {
|
||||
|
||||
if (range1_max - range_lower > 1000000) {
|
||||
shared_memory.jammer_ranges[i].center = range_lower + (1000000/2);
|
||||
shared_memory.jammer_ranges[i].width = 1000000 / 10;
|
||||
shared_memory.jammer_ranges[i].active = true;
|
||||
shared_memory.jammer_ranges[i].duration = 2280000 / 10;
|
||||
range_lower += 1000000;
|
||||
range_bw = abs(frequency_range[c].max - frequency_range[c].min); // Total bw for range
|
||||
if (frequency_range[c].min < frequency_range[c].max)
|
||||
start_freq = frequency_range[c].min;
|
||||
else
|
||||
start_freq = frequency_range[c].max;
|
||||
if (range_bw > 500000) {
|
||||
// Example: 600kHz
|
||||
// int(600000 / 500000) = 2
|
||||
// CH-BW = 600000 / 2 = 300000
|
||||
// Center-A = min + CH-BW / 2 = 150000
|
||||
// BW-A = CH-BW = 300000
|
||||
// Center-B = min + CH-BW + Center-A = 450000
|
||||
// BW-B = CH-BW = 300000
|
||||
num_ranges = 0;
|
||||
while (range_bw > 500000) {
|
||||
range_bw -= 500000;
|
||||
num_ranges++;
|
||||
}
|
||||
ch_width = range_bw / num_ranges;
|
||||
for (c = 0; c < num_ranges; c++) {
|
||||
if (i >= 9) {
|
||||
out_of_ranges = true;
|
||||
break;
|
||||
}
|
||||
jammer_ranges[i].enabled = true;
|
||||
jammer_ranges[i].width = ch_width;
|
||||
jammer_ranges[i].center = start_freq + (ch_width / 2) + (ch_width * c);
|
||||
jammer_ranges[i].duration = 2280000 / 10; // ?
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
shared_memory.jammer_ranges[i].center = (range1_max + range_lower) / 2;
|
||||
shared_memory.jammer_ranges[i].width = (range1_max - range_lower) / 10; // ?
|
||||
shared_memory.jammer_ranges[i].active = true;
|
||||
shared_memory.jammer_ranges[i].duration = 2280000 / 10;
|
||||
//break;
|
||||
if (i >= 9) {
|
||||
out_of_ranges = true;
|
||||
} else {
|
||||
jammer_ranges[i].enabled = true;
|
||||
jammer_ranges[i].width = range_bw;
|
||||
jammer_ranges[i].center = start_freq + (range_bw / 2);
|
||||
jammer_ranges[i].duration = 2280000 / 10; // ?
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// }
|
||||
|
||||
// Swap
|
||||
if (range2_min > range2_max) {
|
||||
t = range2_min;
|
||||
range2_min = range2_max;
|
||||
range2_max = t;
|
||||
}
|
||||
i = 1;
|
||||
range_lower = range2_min;
|
||||
// for (i = 0; i < 3; i++) {
|
||||
if (!out_of_ranges) {
|
||||
if (jamming == true) {
|
||||
jamming = false;
|
||||
button_transmit.set_style(&style_val);
|
||||
button_transmit.set_text("START");
|
||||
radio::disable();
|
||||
} else {
|
||||
jamming = true;
|
||||
button_transmit.set_style(&style_cancel);
|
||||
button_transmit.set_text("STOP");
|
||||
|
||||
if (range1_max - range_lower > 1000000) {
|
||||
shared_memory.jammer_ranges[i].center = range_lower + (1000000/2);
|
||||
shared_memory.jammer_ranges[i].width = 1000000 / 10;
|
||||
shared_memory.jammer_ranges[i].active = true;
|
||||
shared_memory.jammer_ranges[i].duration = 2280000 / 10;
|
||||
range_lower += 1000000;
|
||||
//transmitter_model.set_tuning_frequency(433920000); // TODO
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 0,
|
||||
.sampling_rate = 1536000U,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
}
|
||||
} else {
|
||||
shared_memory.jammer_ranges[i].center = (range1_max + range_lower) / 2;
|
||||
shared_memory.jammer_ranges[i].width = (range1_max - range_lower) / 10; // ?
|
||||
shared_memory.jammer_ranges[i].active = true;
|
||||
shared_memory.jammer_ranges[i].duration = 2280000 / 10;
|
||||
//break;
|
||||
nav.display_modal("Error", "Jamming bandwidth too high.");
|
||||
}
|
||||
// }
|
||||
|
||||
// Swap
|
||||
if (range3_min > range3_max) {
|
||||
t = range3_min;
|
||||
range3_min = range3_max;
|
||||
range3_max = t;
|
||||
}
|
||||
i = 2;
|
||||
range_lower = range3_min;
|
||||
// for (i = 0; i < 3; i++) {
|
||||
|
||||
if (range1_max - range_lower > 1000000) {
|
||||
shared_memory.jammer_ranges[i].center = range_lower + (1000000/2);
|
||||
shared_memory.jammer_ranges[i].width = 1000000 / 10;
|
||||
shared_memory.jammer_ranges[i].active = true;
|
||||
shared_memory.jammer_ranges[i].duration = 2280000 / 10;
|
||||
range_lower += 1000000;
|
||||
} else {
|
||||
shared_memory.jammer_ranges[i].center = (range1_max + range_lower) / 2;
|
||||
shared_memory.jammer_ranges[i].width = (range1_max - range_lower) / 10; // ?
|
||||
shared_memory.jammer_ranges[i].active = true;
|
||||
shared_memory.jammer_ranges[i].duration = 2280000 / 10;
|
||||
//break;
|
||||
}
|
||||
// }
|
||||
|
||||
if (jamming == true) {
|
||||
jamming = false;
|
||||
button_transmit.set_style(&style_val);
|
||||
button_transmit.set_text("START");
|
||||
radio::disable();
|
||||
} else {
|
||||
jamming = true;
|
||||
button_transmit.set_style(&style_cancel);
|
||||
button_transmit.set_text("STOP");
|
||||
|
||||
/*baseband::set_jammer_data(
|
||||
|
||||
);*/
|
||||
|
||||
//transmitter_model.set_tuning_frequency(433920000); // TODO
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 0,
|
||||
.sampling_rate = 1536000U,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -34,35 +34,26 @@ public:
|
||||
JammerView(NavigationView& nav);
|
||||
~JammerView();
|
||||
|
||||
void updfreq(uint8_t id, rf::Frequency f);
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Jammer"; };
|
||||
|
||||
private:
|
||||
void on_retune(const int64_t freq);
|
||||
|
||||
rf::Frequency range1_min;
|
||||
rf::Frequency range1_max;
|
||||
rf::Frequency range2_min;
|
||||
rf::Frequency range2_max;
|
||||
rf::Frequency range3_min;
|
||||
rf::Frequency range3_max;
|
||||
|
||||
rf::Frequency range1_center;
|
||||
rf::Frequency range1_width;
|
||||
rf::Frequency range2_center;
|
||||
rf::Frequency range2_width;
|
||||
rf::Frequency range3_center;
|
||||
rf::Frequency range3_width;
|
||||
|
||||
typedef struct rangepreset {
|
||||
bool active;
|
||||
// range_t from utility.hpp is const only
|
||||
typedef struct freq_range {
|
||||
bool enabled;
|
||||
rf::Frequency min;
|
||||
rf::Frequency max;
|
||||
} rangepreset;
|
||||
} freq_range_t;
|
||||
|
||||
const rangepreset range_presets[10][3] = {
|
||||
freq_range_t frequency_range[3];
|
||||
|
||||
void update_text(uint8_t id, rf::Frequency f);
|
||||
void on_retune(const int64_t freq);
|
||||
|
||||
// TODO: TDD UMTS, voir doc Arcep
|
||||
// TODO: Wifi, BT: 2 400 et 2 483,5 MHz
|
||||
const freq_range_t range_presets[10][3] = {
|
||||
// Orange
|
||||
{{ true, 935000000, 945000000 }, // GSM 900
|
||||
{ true, 1808000000, 1832000000 }, // GSM 1800
|
||||
@ -80,7 +71,7 @@ private:
|
||||
|
||||
// Free
|
||||
{{ true, 945000000, 950000000 }, // GSM 900
|
||||
{ false, 0, 0 }, // GSM 1800
|
||||
{ false, 0, 0 }, // GSM 1800 ?
|
||||
{ true, 2144900000, 2149900000 }}, // UMTS
|
||||
|
||||
// GSM-R
|
||||
@ -88,9 +79,6 @@ private:
|
||||
{ false, 0, 0 }, // GSM 1800
|
||||
{ false, 0, 0 }}, // UMTS
|
||||
|
||||
// TODO: TDD UMTS, voir doc Arcep
|
||||
// TODO: Wifi, BT: 2 400 et 2 483,5 MHz
|
||||
|
||||
// DECT
|
||||
{{ true, 1880000000, 1900000000 }, // BW: 20MHz
|
||||
{ false, 0, 0 },
|
||||
@ -102,12 +90,12 @@ private:
|
||||
{ false, 0, 0 }},
|
||||
|
||||
// ISM 433
|
||||
{{ true, 433050000, 434790000 }, // BW: 0.2%
|
||||
{{ true, 433050000, 434790000 }, // Center: 433.92MHz BW: 0.2%
|
||||
{ false, 0, 0 },
|
||||
{ false, 0, 0 }},
|
||||
|
||||
// Sigfox
|
||||
{{ true, 868150000, 868250000 }, // BW: 40kHz (50kHz)
|
||||
{{ true, 868000000, 868220000 }, // Center: 868.2MHz BW: 40kHz
|
||||
{ false, 0, 0 },
|
||||
{ false, 0, 0 }},
|
||||
|
||||
@ -131,7 +119,7 @@ private:
|
||||
{
|
||||
{ "Ramp ", 0 },
|
||||
{ "FM ", 1 },
|
||||
{ "PSK ", 2 },
|
||||
{ "Phase", 2 },
|
||||
{ "Tones", 3 }
|
||||
}
|
||||
};
|
||||
@ -209,40 +197,18 @@ private:
|
||||
"Range 3"
|
||||
};
|
||||
|
||||
Button button_setfreq1_min {
|
||||
{ 13 * 8, 6 * 16 - 4 - 1, 11 * 8, 18 },
|
||||
"----.----M"
|
||||
};
|
||||
Button button_setfreq1_max {
|
||||
{ 13 * 8, 7 * 16 - 4, 11 * 8, 18 },
|
||||
"----.----M"
|
||||
};
|
||||
std::array<Button, 6> buttons_freq;
|
||||
|
||||
Text text_info1 {
|
||||
{ 3 * 8, 8 * 16 - 4 + 2, 25 * 8, 16 },
|
||||
"C:----.----M W:-----kHz"
|
||||
};
|
||||
|
||||
Button button_setfreq2_min {
|
||||
{ 13 * 8, 9 * 16 - 1, 11 * 8, 18 },
|
||||
"----.----M"
|
||||
};
|
||||
Button button_setfreq2_max {
|
||||
{ 13 * 8, 10 * 16, 11 * 8, 18 },
|
||||
"----.----M"
|
||||
};
|
||||
Text text_info2 {
|
||||
{ 3 * 8, 11 * 16 + 2, 25 * 8, 16 },
|
||||
"C:----.----M W:-----kHz"
|
||||
};
|
||||
|
||||
Button button_setfreq3_min {
|
||||
{ 13 * 8, 12 * 16 + 4 - 1, 11 * 8, 18 },
|
||||
"----.----M"
|
||||
};
|
||||
Button button_setfreq3_max {
|
||||
{ 13 * 8, 13 * 16 + 4, 11 * 8, 18 },
|
||||
"----.----M"
|
||||
};
|
||||
Text text_info3 {
|
||||
{ 3 * 8, 14 * 16 + 4 + 2, 25 * 8, 16 },
|
||||
"C:----.----M W:-----kHz"
|
||||
|
@ -35,8 +35,9 @@
|
||||
#include "ui_setup.hpp"
|
||||
#include "ui_debug.hpp"
|
||||
|
||||
#include "ui_numbers.hpp"
|
||||
//#include "ui_closecall.hpp" // DEBUG
|
||||
//#include "ui_freqman.hpp" // DEBUG
|
||||
#include "ui_freqman.hpp"
|
||||
#include "ui_nuoptix.hpp"
|
||||
#include "ui_soundboard.hpp"
|
||||
|
||||
@ -51,7 +52,6 @@
|
||||
#include "ui_adsbtx.hpp"
|
||||
#include "ui_jammer.hpp"
|
||||
|
||||
#include "analog_audio_app.hpp"
|
||||
#include "ais_app.hpp"
|
||||
#include "ert_app.hpp"
|
||||
#include "tpms_app.hpp"
|
||||
@ -143,7 +143,7 @@ void SystemStatusView::on_camera() {
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i=0; i<320; i++) {
|
||||
for (int i=0; i<320; i++) {
|
||||
std::array<ColorRGB888, 240> row;
|
||||
portapack::display.read_pixels({ 0, i, 240, 1 }, row);
|
||||
png.write_scanline(row);
|
||||
@ -233,14 +233,14 @@ TranspondersMenuView::TranspondersMenuView(NavigationView& nav) {
|
||||
/* ReceiverMenuView ******************************************************/
|
||||
|
||||
ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
|
||||
add_items<7>({ {
|
||||
add_items<6>({ {
|
||||
// { "AFSK", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } }, // AFSKRXView
|
||||
{ "Audio", ui::Color::green(), [&nav](){ nav.push<AnalogAudioView>(); } },
|
||||
{ "Transponders", ui::Color::green(), [&nav](){ nav.push<TranspondersMenuView>(); } },
|
||||
{ "POCSAG 1200", ui::Color::cyan(), [&nav](){ nav.push<POCSAGAppView>(); } },
|
||||
{ "Nordic/BTLE", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "SIGFOX", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } }, // SIGFRXView
|
||||
{ "CCIR", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } }, // XylosRXView
|
||||
{ "AFSK", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } }, // AFSKRXView
|
||||
{ "Nordic/BTLE", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "POCSAG 1200", ui::Color::cyan(), [&nav](){ nav.push<POCSAGAppView>(); } },
|
||||
{ "SIGFOX", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } }, // SIGFRXView
|
||||
{ "Transponders", ui::Color::green(), [&nav](){ nav.push<TranspondersMenuView>(); } },
|
||||
} });
|
||||
on_left = [&nav](){ nav.pop(); };
|
||||
}
|
||||
@ -264,8 +264,8 @@ TransmitterCodedMenuView::TransmitterCodedMenuView(NavigationView& nav) {
|
||||
|
||||
TransmitterAudioMenuView::TransmitterAudioMenuView(NavigationView& nav) {
|
||||
add_items<4>({ {
|
||||
{ "Soundboard", ui::Color::yellow(), [&nav](){ nav.push<SoundBoardView>(); } },
|
||||
{ "Numbers station", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } }, //nav.push<NumbersStationView>();
|
||||
{ "Soundboard", ui::Color::green(), [&nav](){ nav.push<SoundBoardView>(); } },
|
||||
{ "Numbers station", ui::Color::yellow(), [&nav](){ nav.push<NumbersStationView>(); } },
|
||||
{ "Microphone", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "Whistle", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
} });
|
||||
@ -275,16 +275,15 @@ TransmitterAudioMenuView::TransmitterAudioMenuView(NavigationView& nav) {
|
||||
/* SystemMenuView ********************************************************/
|
||||
|
||||
SystemMenuView::SystemMenuView(NavigationView& nav) {
|
||||
add_items<10>({ {
|
||||
add_items<11>({ {
|
||||
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(false); } },
|
||||
{ "Receivers", ui::Color::cyan(), [&nav](){ nav.push<ReceiverMenuView>(); } },
|
||||
{ "Capture", ui::Color::cyan(), [&nav](){ nav.push<CaptureAppView>(); } },
|
||||
{ "Code transmitters", ui::Color::purple(), [&nav](){ nav.push<TransmitterCodedMenuView>(); } },
|
||||
{ "Audio transmitters", ui::Color::purple(), [&nav](){ nav.push<TransmitterAudioMenuView>(); } },
|
||||
{ "Code transmitters", ui::Color::green(), [&nav](){ nav.push<TransmitterCodedMenuView>(); } },
|
||||
{ "Audio transmitters", ui::Color::green(), [&nav](){ nav.push<TransmitterAudioMenuView>(); } },
|
||||
//{ "Close Call RX", ui::Color::cyan(), [&nav](){ nav.push<CloseCallView>(); } },
|
||||
{ "Jammer", ui::Color::orange(), [&nav](){ nav.push<JammerView>(); } },
|
||||
//{ "Frequency manager", ui::Color::white(), [&nav](){ nav.push<FreqManView>(); } },
|
||||
//{ "EPAR TX", ui::Color::green(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, EPAR); } },
|
||||
{ "Frequency manager", ui::Color::white(), [&nav](){ nav.push<FreqManView>(); } },
|
||||
//{ "Analyze", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "Setup", ui::Color::white(), [&nav](){ nav.push<SetupMenuView>(); } },
|
||||
{ "Debug", ui::Color::white(), [&nav](){ nav.push<DebugMenuView>(); } },
|
||||
|
@ -23,20 +23,15 @@
|
||||
#include "ui_numbers.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
#include "evtimer.h"
|
||||
|
||||
#include "ff.h"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "radio.hpp"
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace portapack;
|
||||
using namespace hackrf::one;
|
||||
|
||||
namespace ui {
|
||||
@ -47,25 +42,96 @@ void NumbersStationView::focus() {
|
||||
|
||||
NumbersStationView::~NumbersStationView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void NumbersStationView::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
}
|
||||
|
||||
void NumbersStationView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
void NumbersStationView::prepare_audio() {
|
||||
if (cnt >= sample_duration) {
|
||||
/*if (!check_loop.value()) {
|
||||
transmitter_model.disable();
|
||||
return;
|
||||
} else {
|
||||
file.seek(44);
|
||||
cnt = 0;
|
||||
}*/
|
||||
|
||||
// DEBUG
|
||||
file.seek(44);
|
||||
cnt = 0;
|
||||
|
||||
}
|
||||
|
||||
file.read(audio_buffer, 1024);
|
||||
|
||||
// Unsigned to signed, pretty stupid :/
|
||||
for (size_t n = 0; n < 1024; n++)
|
||||
audio_buffer[n] -= 0x80;
|
||||
|
||||
cnt += 1024;
|
||||
|
||||
baseband::set_fifo_data(audio_buffer);
|
||||
}
|
||||
|
||||
void NumbersStationView::play_sound(uint16_t id) {
|
||||
uint32_t divider;
|
||||
|
||||
if (sounds[id].size == 0) return;
|
||||
|
||||
auto error = file.open("/numbers/" + filenames[id] + ".wav");
|
||||
if (error.is_valid()) return;
|
||||
|
||||
sample_duration = sounds[id].sample_duration;
|
||||
|
||||
cnt = 0;
|
||||
file.seek(44); // Skip header
|
||||
|
||||
prepare_audio();
|
||||
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 0,
|
||||
.sampling_rate = 1536000,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_lna(40);
|
||||
transmitter_model.set_vga(40);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
divider = (1536000 / 44100) - 1;
|
||||
|
||||
baseband::set_audiotx_data(
|
||||
divider,
|
||||
number_bw.value(),
|
||||
false,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
NumbersStationView::NumbersStationView(
|
||||
NavigationView& nav,
|
||||
TransmitterModel& transmitter_model
|
||||
) : transmitter_model(transmitter_model)
|
||||
{
|
||||
NavigationView& nav
|
||||
) {
|
||||
uint8_t m, d, dayofweek;
|
||||
uint16_t y;
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
|
||||
|
||||
add_children({ {
|
||||
&text_title,
|
||||
&number_bw,
|
||||
&button_exit
|
||||
} });
|
||||
|
||||
number_bw.set_value(15);
|
||||
|
||||
rtc::RTC datetime;
|
||||
rtcGetTime(&RTCD1, &datetime);
|
||||
|
||||
|
@ -20,34 +20,66 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UI_NUMBERS_H__
|
||||
#define __UI_NUMBERS_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
#include "message.hpp"
|
||||
#include "rf_path.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "file.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class NumbersStationView : public View {
|
||||
public:
|
||||
NumbersStationView(NavigationView& nav, TransmitterModel& transmitter_model);
|
||||
NumbersStationView(NavigationView& nav);
|
||||
~NumbersStationView();
|
||||
|
||||
void focus() override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
std::string title() const override { return "Numbers station"; };
|
||||
|
||||
private:
|
||||
TransmitterModel& transmitter_model;
|
||||
// Different from the one in ui_soundboard.hpp, simpler
|
||||
struct sound {
|
||||
uint32_t size = 0;
|
||||
uint32_t sample_duration = 0;
|
||||
};
|
||||
|
||||
sound sounds[11];
|
||||
|
||||
const std::string filenames[11] = {
|
||||
"zero",
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
"four",
|
||||
"five",
|
||||
"six",
|
||||
"seven",
|
||||
"eight",
|
||||
"nine",
|
||||
"anounce"
|
||||
};
|
||||
|
||||
const uint8_t month_table[12] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
|
||||
const char * day_of_week[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
|
||||
|
||||
File file;
|
||||
uint32_t cnt;
|
||||
uint32_t sample_duration;
|
||||
int8_t audio_buffer[1024];
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void prepare_audio();
|
||||
void play_sound(uint16_t id);
|
||||
|
||||
// Schedule: save on sd card
|
||||
// For each day of the week, max 8 messages ?
|
||||
// For each message: Normal, accent. Can chose accent on first or last digit
|
||||
@ -63,10 +95,30 @@ private:
|
||||
"Schedule:"
|
||||
};
|
||||
|
||||
NumberField number_bw {
|
||||
{ 11 * 8, 3 * 16 },
|
||||
3,
|
||||
{1, 150},
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
Button button_exit {
|
||||
{ 21 * 8, 16 * 16, 64, 32 },
|
||||
"Exit"
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal {
|
||||
Message::ID::FIFOSignal,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const FIFOSignalMessage*>(p);
|
||||
if (message->signaltype == 1) {
|
||||
this->prepare_audio();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_NUMBERS_H__*/
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
#include "ui_rds.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
#include "rds.hpp"
|
||||
#include "ff.h"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack.hpp"
|
||||
@ -35,6 +35,7 @@
|
||||
#include <cstring>
|
||||
|
||||
using namespace portapack;
|
||||
using namespace rds;
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -42,186 +43,49 @@ void RDSView::focus() {
|
||||
button_editpsn.focus();
|
||||
}
|
||||
|
||||
void RDSView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
RDSView::~RDSView() {
|
||||
radio::disable();
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
std::string to_string_bin(const uint32_t n, const uint8_t l) {
|
||||
char p[33];
|
||||
for (uint8_t c = 0; c < l; c++) {
|
||||
if ((n << c) & (1 << l))
|
||||
p[c] = '1';
|
||||
else
|
||||
p[c] = '0';
|
||||
}
|
||||
p[l] = 0;
|
||||
return p;
|
||||
}
|
||||
void RDSView::start_tx() {
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 0,
|
||||
.sampling_rate = 2280000,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_lna(40);
|
||||
transmitter_model.set_vga(40);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
uint32_t makeblock(uint32_t blockdata, uint16_t offset) {
|
||||
uint16_t CRC = 0;
|
||||
uint8_t doinv;
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
doinv = (((blockdata << i) & 0x8000) >> 15) ^ (CRC >> 9);
|
||||
if (doinv) CRC ^= 0b0011011100;
|
||||
CRC = ((CRC << 1) | doinv) & 0x3FF;
|
||||
}
|
||||
|
||||
return (blockdata << 10) | (CRC ^ offset);
|
||||
}
|
||||
|
||||
// Todo:
|
||||
// Make PI
|
||||
// Set frequency
|
||||
// TA/TP flags
|
||||
// Group selection
|
||||
|
||||
uint8_t RDSView::b2b(const bool in) {
|
||||
if (in)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RDSView::make_0B_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA,
|
||||
const bool MS, const bool DI, const uint8_t C, const char * chars) {
|
||||
|
||||
group[0] = PI_code;
|
||||
group[1] = (0x0 << 12) | (1 << 11) | (b2b(TP) << 10) | (PTY << 5) | (b2b(TA) << 4) | (b2b(MS) << 3) | (b2b(DI) << 2) | (C & 3);
|
||||
group[2] = PI_code;
|
||||
group[3] = (chars[0] << 8) | chars[1];
|
||||
}
|
||||
|
||||
void RDSView::make_2A_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB,
|
||||
const bool segment, const char * chars) {
|
||||
|
||||
group[0] = PI_code;
|
||||
group[1] = (0x0 << 12) | (1 << 11) | (b2b(TP) << 10) | (PTY << 5) | (b2b(AB) << 4) | (segment & 15);
|
||||
group[2] = (chars[0] << 8) | chars[1];
|
||||
group[3] = (chars[2] << 8) | chars[3];
|
||||
}
|
||||
|
||||
void RDSView::gen_PSN(const char * psname) {
|
||||
uint8_t c;
|
||||
uint32_t group[4][4] = { 0 };
|
||||
|
||||
make_0B_group(&group[0][0], 0xF849, true, options_pty.selected_index(), false, true, false, 0, &psname[0]);
|
||||
make_0B_group(&group[1][0], 0xF849, true, options_pty.selected_index(), false, true, false, 1, &psname[2]);
|
||||
make_0B_group(&group[2][0], 0xF849, true, options_pty.selected_index(), false, true, false, 2, &psname[4]);
|
||||
make_0B_group(&group[3][0], 0xF849, true, options_pty.selected_index(), false, true, false, 3, &psname[6]);
|
||||
|
||||
/*uint32_t group[4][4] = {
|
||||
{
|
||||
0b1111100001001001, //PI
|
||||
0b0000110011101000, //Address
|
||||
0b1111100001001001, //PI
|
||||
0b0000000000000000 //Replaced
|
||||
},
|
||||
|
||||
{
|
||||
0b1111100001001001, //PI
|
||||
0b0000110011101001, //Address
|
||||
0b1111100001001001, //PI
|
||||
0b0000000000000000 //Replaced
|
||||
},
|
||||
|
||||
{
|
||||
0b1111100001001001, //PI
|
||||
0b0000110011101010, //Address
|
||||
0b1111100001001001, //PI
|
||||
0b0000000000000000 //Replaced
|
||||
},
|
||||
|
||||
{
|
||||
0b1111100001001001, //PI
|
||||
0b0000110011101011, //Address
|
||||
0b1111100001001001, //PI
|
||||
0b0000000000000000 //Replaced
|
||||
},
|
||||
};
|
||||
|
||||
//Insert PSN data in groups
|
||||
group[0][3] = (psname[0] << 8) | psname[1];
|
||||
group[1][3] = (psname[2] << 8) | psname[3];
|
||||
group[2][3] = (psname[4] << 8) | psname[5];
|
||||
group[3][3] = (psname[6] << 8) | psname[7];
|
||||
*/
|
||||
|
||||
// Generate checkbits
|
||||
for (c = 0; c < 4; c++) {
|
||||
group[c][0] = makeblock(group[c][0], RDS_OFFSET_A);
|
||||
group[c][1] = makeblock(group[c][1], RDS_OFFSET_B);
|
||||
group[c][2] = makeblock(group[c][2], RDS_OFFSET_Cp); // C' !
|
||||
group[c][3] = makeblock(group[c][3], RDS_OFFSET_D);
|
||||
}
|
||||
|
||||
// Todo
|
||||
//for (c = 0; c < 16; c++)
|
||||
// shared_memory.radio_data[c] = group[c >> 2][c & 3];
|
||||
|
||||
shared_memory.bit_length = 4 * 4 * 26;
|
||||
}
|
||||
|
||||
void RDSView::gen_RadioText(const char * radiotext) {
|
||||
size_t c, i;
|
||||
uint32_t * group;
|
||||
char radiotext_buffer[65] = { 0 };
|
||||
uint8_t rtlen, groups;
|
||||
|
||||
strcpy(radiotext_buffer, radiotext);
|
||||
|
||||
rtlen = strlen(radiotext_buffer);
|
||||
|
||||
radiotext_buffer[rtlen] = 0x0D;
|
||||
|
||||
// Pad to multiple of 4
|
||||
while(rtlen & 3) {
|
||||
radiotext_buffer[rtlen] = ' ';
|
||||
rtlen++;
|
||||
}
|
||||
|
||||
groups = rtlen >> 2; // 4 characters per group
|
||||
|
||||
group = (uint32_t*)chHeapAlloc(0x0, 4 * groups * sizeof(uint32_t));
|
||||
|
||||
for (c = 0; c < groups; c++)
|
||||
make_2A_group(&group[c << 2], 0xF849, true, options_pty.selected_index(), false, c, &radiotext_buffer[c << 2]);
|
||||
|
||||
// Generate checkbits
|
||||
for (c = 0; c < groups; c++) {
|
||||
i = c * 4;
|
||||
group[i + 0] = makeblock(group[i + 0], RDS_OFFSET_A);
|
||||
group[i + 1] = makeblock(group[i + 1], RDS_OFFSET_B);
|
||||
group[i + 2] = makeblock(group[i + 2], RDS_OFFSET_C);
|
||||
group[i + 3] = makeblock(group[i + 3], RDS_OFFSET_D);
|
||||
}
|
||||
|
||||
// Todo
|
||||
//for (c = 0; c < (groups * 4); c++)
|
||||
// shared_memory.radio_data[c] = group[c];
|
||||
|
||||
shared_memory.bit_length = groups * 4 * 26;
|
||||
baseband::set_rds_data(message_length);
|
||||
}
|
||||
|
||||
void RDSView::paint(Painter& painter) {
|
||||
char RadioTextA[17];
|
||||
|
||||
(void)painter;
|
||||
|
||||
text_psn.set(PSN);
|
||||
|
||||
memcpy(RadioTextA, RadioText, 16);
|
||||
RadioTextA[16] = 0;
|
||||
text_radiotexta.set(RadioTextA);
|
||||
text_radiotextb.set(&RadioText[16]);
|
||||
memcpy(RadioTextA, RadioText + 16, 8);
|
||||
RadioTextA[8] = 0;
|
||||
text_radiotextb.set(RadioTextA);
|
||||
}
|
||||
|
||||
RDSView::RDSView(NavigationView& nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_rds);
|
||||
|
||||
strcpy(PSN, "TEST1234");
|
||||
strcpy(RadioText, "Radiotext test !");
|
||||
strcpy(RadioText, "Radiotext test ABCD1234");
|
||||
|
||||
add_children({ {
|
||||
&field_frequency,
|
||||
@ -239,12 +103,16 @@ RDSView::RDSView(NavigationView& nav) {
|
||||
&button_exit
|
||||
} });
|
||||
|
||||
rds_radio_config.tuning_frequency = tuning_frequency;
|
||||
field_frequency.set_step(100000);
|
||||
field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
field_frequency.set_step(50000); // 50kHz steps
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(tuning_frequency);
|
||||
auto new_view = nav.push<FrequencyKeypadView>(field_frequency.value());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->field_frequency.set_value(f);
|
||||
this->on_tuning_frequency_changed(f);
|
||||
};
|
||||
};
|
||||
|
||||
@ -257,16 +125,15 @@ RDSView::RDSView(NavigationView& nav) {
|
||||
};
|
||||
button_txpsn.on_select = [this](Button&){
|
||||
if (txing) {
|
||||
radio::disable();
|
||||
button_txpsn.set_text("PSN");
|
||||
button_txradiotext.set_text("Radiotext");
|
||||
transmitter_model.disable();
|
||||
txing = false;
|
||||
} else {
|
||||
gen_PSN(PSN);
|
||||
rds_radio_config.tuning_frequency = tuning_frequency;
|
||||
message_length = gen_PSN(PSN, options_pty.selected_index());
|
||||
button_txpsn.set_text("STOP");
|
||||
txing = true;
|
||||
radio::disable();
|
||||
start_tx();
|
||||
}
|
||||
};
|
||||
|
||||
@ -275,16 +142,15 @@ RDSView::RDSView(NavigationView& nav) {
|
||||
};
|
||||
button_txradiotext.on_select = [this](Button&){
|
||||
if (txing) {
|
||||
radio::disable();
|
||||
button_txpsn.set_text("PSN");
|
||||
button_txradiotext.set_text("Radiotext");
|
||||
transmitter_model.disable();
|
||||
txing = false;
|
||||
} else {
|
||||
gen_RadioText(RadioText);
|
||||
rds_radio_config.tuning_frequency = tuning_frequency;
|
||||
message_length = gen_RadioText(RadioText, options_pty.selected_index());
|
||||
button_txradiotext.set_text("STOP");
|
||||
txing = true;
|
||||
radio::enable(rds_radio_config);
|
||||
start_tx();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -33,12 +33,6 @@
|
||||
#include "volume.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
|
||||
#define RDS_OFFSET_A 0b0011111100
|
||||
#define RDS_OFFSET_B 0b0110011000
|
||||
#define RDS_OFFSET_C 0b0101101000
|
||||
#define RDS_OFFSET_Cp 0b1101010000
|
||||
#define RDS_OFFSET_D 0b0110110100
|
||||
|
||||
namespace ui {
|
||||
|
||||
class RDSView : public View {
|
||||
@ -54,28 +48,11 @@ private:
|
||||
char PSN[9];
|
||||
char RadioText[25];
|
||||
bool txing = false;
|
||||
int64_t tuning_frequency = 92200000; // TODO: CHANGE !
|
||||
|
||||
uint8_t b2b(const bool in);
|
||||
uint16_t message_length;
|
||||
|
||||
void gen_PSN(const char * psname);
|
||||
void gen_RadioText(const char * radiotext);
|
||||
|
||||
void make_0B_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA,
|
||||
const bool MS, const bool DI, const uint8_t C, const char * chars);
|
||||
void make_2A_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB,
|
||||
const bool segment, const char * chars);
|
||||
|
||||
radio::Configuration rds_radio_config = {
|
||||
0,
|
||||
2280000, // ?
|
||||
2500000, // ?
|
||||
rf::Direction::Transmit,
|
||||
true,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
};
|
||||
void start_tx();
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 1 * 8, 1 * 16 },
|
||||
|
@ -25,8 +25,6 @@
|
||||
#include "ui_soundboard.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
#include "file.hpp"
|
||||
|
||||
#include "lfsr_random.hpp"
|
||||
#include "ui_alphanum.hpp"
|
||||
#include "portapack.hpp"
|
||||
@ -258,6 +256,11 @@ SoundBoardView::SoundBoardView(
|
||||
}
|
||||
}
|
||||
|
||||
if (!c) {
|
||||
nav.display_modal("No files", "No files in /wav/ directory");
|
||||
nav.pop();
|
||||
}
|
||||
|
||||
max_sound = c;
|
||||
max_page = max_sound / 21; // 21 buttons per page
|
||||
|
||||
|
@ -329,8 +329,6 @@ XylosView::XylosView(NavigationView& nav) {
|
||||
generate_message();
|
||||
};
|
||||
|
||||
subfamily_code.hidden(true);
|
||||
text_subfamily.set_style(&style_grey);
|
||||
checkbox_wcsubfamily.on_select = [this](Checkbox&) {
|
||||
if (checkbox_wcsubfamily.value() == true) {
|
||||
receiver_code.set_focusable(false);
|
||||
@ -342,8 +340,6 @@ XylosView::XylosView(NavigationView& nav) {
|
||||
generate_message();
|
||||
};
|
||||
|
||||
receiver_code.hidden(true);
|
||||
text_receiver.set_style(&style_grey);
|
||||
checkbox_wcid.on_select = [this](Checkbox&) {
|
||||
if (checkbox_wcid.value() == true) {
|
||||
receiver_code.set_focusable(false);
|
||||
|
Binary file not shown.
@ -50,9 +50,12 @@ void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (!bit_part) {
|
||||
if (bit_pos >= 112) {
|
||||
// Stop
|
||||
message.n = 200;
|
||||
shared_memory.application_queue.push(message);
|
||||
configured = false;
|
||||
cur_bit = 0;
|
||||
} else {
|
||||
cur_bit = shared_memory.tx_data[bit_pos];
|
||||
cur_bit = 0; //shared_memory.tx_data[bit_pos];
|
||||
bit_pos++;
|
||||
bit_part = 1;
|
||||
}
|
||||
@ -66,7 +69,7 @@ void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
// 1001010110100110 0110010110010101
|
||||
|
||||
if (cur_bit) {
|
||||
phase = (phase + 0x1FE0000); // What ?
|
||||
phase = (phase + 0x1FE00); // What ?
|
||||
sphase = phase + (64 << 18);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
||||
@ -78,17 +81,13 @@ void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
buffer.p[i] = {(int8_t)re, (int8_t)im};
|
||||
}
|
||||
|
||||
// TEST
|
||||
message.n = 200;
|
||||
shared_memory.application_queue.push(message);
|
||||
configured = false;
|
||||
}
|
||||
|
||||
void ADSBTXProcessor::on_message(const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ADSBConfigureMessage*>(p);
|
||||
|
||||
if (message.id == Message::ID::ADSBConfigure) {
|
||||
bit_part = 0;
|
||||
bit_pos = 0;
|
||||
cur_bit = 0;
|
||||
preamble = true;
|
||||
|
@ -93,7 +93,7 @@ void DTMFTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
}
|
||||
|
||||
void DTMFTXProcessor::on_message(const Message* const msg) {
|
||||
char * tone_ptr;
|
||||
uint8_t * tone_ptr;
|
||||
const auto message = *reinterpret_cast<const DTMFTXConfigMessage*>(msg);
|
||||
|
||||
if (message.id == Message::ID::DTMFTXConfig) {
|
||||
|
@ -40,11 +40,11 @@ void JammerProcessor::execute(const buffer_c8_t& buffer) {
|
||||
for (;;) {
|
||||
ir++;
|
||||
if (ir > 15) ir = 0;
|
||||
if (shared_memory.jammer_ranges[ir].active == true) break;
|
||||
if (jammer_ranges[ir].enabled == true) break;
|
||||
}
|
||||
jammer_bw = shared_memory.jammer_ranges[ir].width / 2;
|
||||
jammer_bw = jammer_ranges[ir].width / 2;
|
||||
|
||||
message.freq = shared_memory.jammer_ranges[ir].center;
|
||||
message.freq = jammer_ranges[ir].center;
|
||||
shared_memory.application_queue.push(message);
|
||||
} else {
|
||||
s++;
|
||||
@ -73,9 +73,9 @@ void JammerProcessor::execute(const buffer_c8_t& buffer) {
|
||||
sample = sine_table_i8[(sphase & 0x03FC0000) >> 18];
|
||||
|
||||
// FM
|
||||
frq = sample * jammer_bw;
|
||||
delta = sample * jammer_bw;
|
||||
|
||||
phase = (phase + frq);
|
||||
phase = (phase + delta);
|
||||
sphase = phase + (64 << 18);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
||||
@ -86,6 +86,9 @@ void JammerProcessor::execute(const buffer_c8_t& buffer) {
|
||||
};
|
||||
|
||||
void JammerProcessor::on_message(const Message* const msg) {
|
||||
|
||||
jammer_ranges = (JammerRange*)shared_memory.tx_data;
|
||||
|
||||
/*const auto message = *reinterpret_cast<const DTMFTXConfigMessage*>(msg);
|
||||
|
||||
if (message.id == Message::ID::DTMFTXConfig) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
class JammerProcessor : public BasebandProcessor {
|
||||
public:
|
||||
@ -37,6 +38,8 @@ private:
|
||||
|
||||
BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
JammerRange * jammer_ranges;
|
||||
|
||||
int32_t lfsr32 = 0xABCDE;
|
||||
uint32_t s;
|
||||
int8_t r, ir, re, im;
|
||||
@ -45,7 +48,7 @@ private:
|
||||
int32_t lfsr;
|
||||
uint32_t sample_count;
|
||||
uint32_t aphase, phase, sphase;
|
||||
int32_t sample, frq;
|
||||
int32_t sample, delta;
|
||||
RetuneMessage message;
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
#include "proc_rds.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
@ -31,18 +31,18 @@ void RDSProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
|
||||
//Sample generation 2.28M/10=228kHz
|
||||
if(s >= 9) {
|
||||
// Sample generation at 2.28M / 10 = 228kHz
|
||||
if (s >= 9) {
|
||||
s = 0;
|
||||
if(sample_count >= SAMPLES_PER_BIT) {
|
||||
if (sample_count >= SAMPLES_PER_BIT) {
|
||||
cur_bit = (rdsdata[(bit_pos / 26) & 15] >> (25 - (bit_pos % 26))) & 1;
|
||||
prev_output = cur_output;
|
||||
cur_output = prev_output ^ cur_bit;
|
||||
|
||||
const int32_t *src = waveform_biphase; // const ok ?
|
||||
const int32_t * src = waveform_biphase;
|
||||
int idx = in_sample_index;
|
||||
|
||||
for(int j = 0; j < FILTER_SIZE; j++) {
|
||||
for (int j = 0; j < FILTER_SIZE; j++) {
|
||||
val = (*src++);
|
||||
if (cur_output) val = -val;
|
||||
sample_buffer[idx++] += val;
|
||||
@ -52,10 +52,11 @@ void RDSProcessor::execute(const buffer_c8_t& buffer) {
|
||||
in_sample_index += SAMPLES_PER_BIT;
|
||||
if (in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE;
|
||||
|
||||
if (bit_pos < shared_memory.bit_length)
|
||||
if (bit_pos < message_length)
|
||||
bit_pos++;
|
||||
else
|
||||
bit_pos = 0;
|
||||
|
||||
sample_count = 0;
|
||||
}
|
||||
|
||||
@ -64,38 +65,39 @@ void RDSProcessor::execute(const buffer_c8_t& buffer) {
|
||||
out_sample_index++;
|
||||
if (out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0;
|
||||
|
||||
//AM @ 228k/4 = 57kHz
|
||||
switch (mphase) {
|
||||
// AM @ 228k/4 = 57kHz
|
||||
// 0, sample, 0, -sample...
|
||||
switch (mphase & 3) {
|
||||
case 0:
|
||||
case 2: sample = 0; break;
|
||||
case 1: break;
|
||||
case 3: sample = -sample; break;
|
||||
case 3: sample = -sample; // break;
|
||||
}
|
||||
mphase++;
|
||||
if (mphase >= 4) mphase = 0;
|
||||
|
||||
sample_count++;
|
||||
} else {
|
||||
s++;
|
||||
}
|
||||
|
||||
//FM
|
||||
frq = (sample>>16) * 386760;
|
||||
// FM
|
||||
delta = (sample >> 16) * 386760; // ?
|
||||
|
||||
phase = (phase + frq);
|
||||
sphase = phase + (256<<16);
|
||||
phase += delta;
|
||||
sphase = phase + (64 << 18);
|
||||
|
||||
re = (sine_table_f32[(sphase & 0x03FF0000)>>18]*127);
|
||||
im = (sine_table_f32[(phase & 0x03FF0000)>>18]*127);
|
||||
re = (sine_table_i8[(sphase & 0x03FF0000) >> 18]);
|
||||
im = (sine_table_i8[(phase & 0x03FF0000) >> 18]);
|
||||
|
||||
buffer.p[i] = {(int8_t)re,(int8_t)im};
|
||||
buffer.p[i] = {(int8_t)re, (int8_t)im};
|
||||
}
|
||||
}
|
||||
|
||||
void RDSProcessor::on_message(const Message* const msg) {
|
||||
if (msg->id == Message::ID::RDSConfigure) {
|
||||
//const auto message = *reinterpret_cast<const RDSConfigureMessage*>(p);
|
||||
const auto message = *reinterpret_cast<const RDSConfigureMessage*>(msg);
|
||||
rdsdata = (uint32_t*)shared_memory.tx_data;
|
||||
message_length = message.length;
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ private:
|
||||
|
||||
BasebandThread baseband_thread { 2280000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
|
||||
uint16_t message_length;
|
||||
int8_t re, im;
|
||||
uint8_t mphase, s;
|
||||
uint32_t bit_pos;
|
||||
@ -54,8 +55,7 @@ private:
|
||||
int32_t sample;
|
||||
int out_sample_index = SAMPLE_BUFFER_SIZE - 1;
|
||||
uint32_t phase, sphase;
|
||||
int32_t sig, frq, frq_im, rdsc;
|
||||
int32_t k;
|
||||
int32_t delta;
|
||||
|
||||
bool configured { false };
|
||||
|
||||
|
@ -55,7 +55,7 @@ void XylosProcessor::execute(const buffer_c8_t& buffer) {
|
||||
message.n = 25; // End of message code
|
||||
shared_memory.application_queue.push(message);
|
||||
} else {
|
||||
message.n = byte_pos; // Inform UI about progress (just as eye candy)
|
||||
message.n = byte_pos; // Inform UI about progress (just as eye candy)
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ void XylosProcessor::execute(const buffer_c8_t& buffer) {
|
||||
re = 0;
|
||||
im = 0;
|
||||
} else {
|
||||
tone_sample = (sine_table_i8[(tone_phase & 0x03FC0000)>>18]);
|
||||
tone_sample = (sine_table_i8[(tone_phase & 0x03FC0000) >> 18]);
|
||||
|
||||
// Audio preview sample generation: 1536000/48000 = 32
|
||||
/*if (as >= 31) {
|
||||
@ -87,16 +87,16 @@ void XylosProcessor::execute(const buffer_c8_t& buffer) {
|
||||
// FM
|
||||
// 1<<18 = 262144
|
||||
// m = (262144 * BW) / 1536000 / 2
|
||||
frq = tone_sample * 853; // 10kHz BW
|
||||
delta = tone_sample * 853; // 10kHz BW
|
||||
|
||||
phase = (phase + frq);
|
||||
sphase = phase + (64<<18);
|
||||
phase += delta;
|
||||
sphase = phase + (64 << 18);
|
||||
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000)>>18]);
|
||||
im = (sine_table_i8[(phase & 0x03FC0000)>>18]);
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
||||
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
|
||||
}
|
||||
|
||||
buffer.p[i] = {(int8_t)re,(int8_t)im};
|
||||
buffer.p[i] = {(int8_t)re, (int8_t)im};
|
||||
}
|
||||
|
||||
//audio_output.write(audio_buffer);
|
||||
|
@ -68,7 +68,7 @@ private:
|
||||
uint8_t digit = 0;
|
||||
uint32_t sample_count = 0;
|
||||
uint32_t tone_phase, phase, sphase;
|
||||
int32_t tone_sample, frq;
|
||||
int32_t tone_sample, delta;
|
||||
bool silence = true;
|
||||
TXDoneMessage message;
|
||||
|
||||
|
@ -295,7 +295,7 @@ void ILI9341::drawBMP(const ui::Point p, const uint8_t * bitmap, const bool tran
|
||||
uint32_t data_idx;
|
||||
uint8_t by, c, count, transp_idx = 0;
|
||||
ui::Color line_buffer[240];
|
||||
ui::Coord px = 0, py;
|
||||
uint16_t px = 0, py;
|
||||
ui::Color palette[16];
|
||||
|
||||
// Abort if bad depth or no RLE
|
||||
|
@ -539,13 +539,13 @@ public:
|
||||
class RDSConfigureMessage : public Message {
|
||||
public:
|
||||
constexpr RDSConfigureMessage(
|
||||
const uint32_t length
|
||||
const uint16_t length
|
||||
) : Message { ID::RDSConfigure },
|
||||
length(length)
|
||||
{
|
||||
}
|
||||
|
||||
const uint32_t length = 0;
|
||||
const uint16_t length = 0;
|
||||
};
|
||||
|
||||
class RetuneMessage : public Message {
|
||||
|
@ -28,9 +28,9 @@
|
||||
#include "message_queue.hpp"
|
||||
|
||||
struct JammerRange {
|
||||
bool active;
|
||||
int64_t center;
|
||||
int64_t width;
|
||||
bool enabled;
|
||||
uint64_t center;
|
||||
uint32_t width;
|
||||
uint32_t duration;
|
||||
};
|
||||
|
||||
@ -47,11 +47,8 @@ struct SharedMemory {
|
||||
|
||||
char m4_panic_msg[32] { 0 };
|
||||
|
||||
size_t bit_length;
|
||||
|
||||
JammerRange jammer_ranges[16];
|
||||
|
||||
char tx_data[512] { 0 };
|
||||
// struct tx_data union for 9x JammerRange ?
|
||||
uint8_t tx_data[512] { 0 };
|
||||
};
|
||||
|
||||
extern SharedMemory& shared_memory;
|
||||
|
@ -103,7 +103,7 @@ void Widget::hidden(bool hide) {
|
||||
if( hide ) {
|
||||
// TODO: Instead of dirtying parent entirely, dirty only children
|
||||
// that overlap with this widget.
|
||||
parent()->dirty_overlapping_children_in_rect(parent_rect);
|
||||
dirty_overlapping_children_in_rect(parent_rect);
|
||||
/* TODO: Notify self and all non-hidden children that they're
|
||||
* now effectively hidden?
|
||||
*/
|
||||
@ -1116,6 +1116,35 @@ SymField::SymField(
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
SymField::SymField(
|
||||
Point parent_pos,
|
||||
size_t length,
|
||||
bool hex
|
||||
) : Widget { { parent_pos, { static_cast<ui::Dim>(8 * length), 16 } } },
|
||||
length_ { length },
|
||||
hex_ { hex }
|
||||
{
|
||||
uint8_t c;
|
||||
|
||||
// Hex field auto-init
|
||||
for (c = 0; c < length; c++)
|
||||
set_symbol_list(c, "0123456789ABCDEF");
|
||||
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
uint64_t SymField::value_hex_u64() {
|
||||
uint8_t c;
|
||||
uint64_t v = 0;
|
||||
|
||||
if (hex_) {
|
||||
for (c = 0; c < length_; c++)
|
||||
v += values_[c] << (4 * (length_ - 1 - c));
|
||||
return v;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t SymField::value(const uint32_t index) {
|
||||
if (index >= length_) return 0;
|
||||
|
||||
|
@ -447,6 +447,7 @@ public:
|
||||
std::function<void()> on_change;
|
||||
|
||||
SymField(Point parent_pos, size_t length);
|
||||
SymField(Point parent_pos, size_t length, bool hex);
|
||||
|
||||
SymField(const SymField&) = delete;
|
||||
SymField(SymField&&) = delete;
|
||||
@ -455,6 +456,7 @@ public:
|
||||
void set_value(const uint32_t index, const uint32_t new_value);
|
||||
void set_length(const uint32_t new_length);
|
||||
void set_symbol_list(const uint32_t index, const std::string symbol_list);
|
||||
uint64_t value_hex_u64();
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
@ -468,6 +470,7 @@ private:
|
||||
uint32_t selected_ = 0;
|
||||
size_t length_, prev_length_;
|
||||
bool erase_prev_ = false;
|
||||
bool hex_ = false;
|
||||
|
||||
int32_t clip_value(const uint32_t index, const uint32_t value);
|
||||
};
|
||||
|
Binary file not shown.
BIN
sdcard/numbers/anounce.wav
Normal file
BIN
sdcard/numbers/anounce.wav
Normal file
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user