Restoring jammer and RDS functionalities, please wait...

Started work on frequency manager and numbers station simulator
This commit is contained in:
furrtek 2016-12-05 12:56:41 +01:00
parent ef0feae62b
commit d18b6d135d
43 changed files with 1083 additions and 734 deletions

View File

@ -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

View File

@ -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"

View 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 */

View 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__*/

View File

@ -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,

View File

@ -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);

View File

@ -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

View 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 */

View 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__*/

View File

@ -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,

View File

@ -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);

View File

@ -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();
}
};

View File

@ -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"

View File

@ -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),

View File

@ -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();

View File

@ -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() {
}
}

View File

@ -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;

View File

@ -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,135 +130,116 @@ 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);
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++;
}
};
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;
ch_width = range_bw / num_ranges;
for (c = 0; c < num_ranges; c++) {
if (i >= 9) {
out_of_ranges = true;
break;
}
// Swap
if (range1_min > range1_max) {
t = range1_min;
range1_min = range1_max;
range1_max = t;
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++;
}
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;
} 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;
}
// }
// 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 (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;
if (i >= 9) {
out_of_ranges = true;
} 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;
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 (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 (!out_of_ranges) {
if (jamming == true) {
jamming = false;
button_transmit.set_style(&style_val);
@ -317,10 +250,6 @@ JammerView::JammerView(NavigationView& nav) {
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,
@ -331,6 +260,10 @@ JammerView::JammerView(NavigationView& nav) {
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
}
} else {
nav.display_modal("Error", "Jamming bandwidth too high.");
}
}
};
button_exit.on_select = [&nav](Button&){

View File

@ -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"

View File

@ -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>(); } },

View File

@ -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);

View File

@ -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__*/

View File

@ -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();
}
};

View File

@ -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 },

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

View File

@ -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;
};

View File

@ -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;
}
}

View File

@ -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 };

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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

Binary file not shown.

Binary file not shown.