# This is a combination of 2 commits.

# The first commit's message is:

Updated RDS transmitter: flags, PI and date/time

Merging baseband audio tone generators

Merging DTMF baseband with "tones" baseband

Added stealth transmit mode

App flash section bumped to 512k
RX and TX LEDs are now used
Play dead should work again, added login option
Morse frame gen. for letters and fox hunt codes
Merged EPAR with Xylos
Made EPAR use encoders for frame gen.
Moved OOK encoders data in encoders.hpp
Simplified about screen, ui_about_demo.* files are still there

BHT city DB, keywords removed

BHT cities DB, keywords removed

Update README.md

RDS radiotext and time group generators

# This is the 2nd commit message:

Update README.md
This commit is contained in:
furrtek 2016-12-09 18:21:47 +01:00
parent 0b13283d5d
commit 6bcb7dc1b1
91 changed files with 3867 additions and 2535 deletions

View File

@ -1,26 +1,35 @@
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 2.8
# Generated by "Unix Makefiles" Generator, CMake Version 3.5
# Default target executed when no arguments are given to make.
default_target: all
.PHONY : default_target
# Allow only one "make -f Makefile2" at a time, but pass parallelism.
.NOTPARALLEL:
#=============================================================================
# Special targets provided by cmake.
# Disable implicit rules so canonical targets will work.
.SUFFIXES:
# Remove some rules from gmake that .SUFFIXES does not remove.
SUFFIXES =
.SUFFIXES: .hpux_make_needs_suffix_list
# Suppress display of executed commands.
$(VERBOSE).SILENT:
# A target that is always out of date.
cmake_force:
.PHONY : cmake_force
#=============================================================================
@ -49,12 +58,13 @@ CMAKE_BINARY_DIR = /home/furrtek/portapack-hackrf
# Special rule for the target edit_cache
edit_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running interactive CMake command-line interface..."
/usr/bin/cmake -i .
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..."
/usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available.
.PHONY : edit_cache
# Special rule for the target edit_cache
edit_cache/fast: edit_cache
.PHONY : edit_cache/fast
# Special rule for the target rebuild_cache
@ -65,6 +75,7 @@ rebuild_cache:
# Special rule for the target rebuild_cache
rebuild_cache/fast: rebuild_cache
.PHONY : rebuild_cache/fast
# The main all target
@ -81,6 +92,7 @@ clean:
# The main clean target
clean/fast: clean
.PHONY : clean/fast
# Prepare targets for installation.
@ -105,6 +117,7 @@ firmware/CMakeFiles/firmware.dir/rule:
# Convenience name for target.
firmware: firmware/CMakeFiles/firmware.dir/rule
.PHONY : firmware
# fast build rule for target.
@ -119,6 +132,7 @@ firmware/CMakeFiles/program.dir/rule:
# Convenience name for target.
program: firmware/CMakeFiles/program.dir/rule
.PHONY : program
# fast build rule for target.
@ -133,6 +147,7 @@ firmware/CMakeFiles/release.dir/rule:
# Convenience name for target.
release: firmware/CMakeFiles/release.dir/rule
.PHONY : release
# fast build rule for target.
@ -149,8 +164,8 @@ help:
@echo "... edit_cache"
@echo "... firmware"
@echo "... program"
@echo "... rebuild_cache"
@echo "... release"
@echo "... rebuild_cache"
.PHONY : help

View File

@ -1,5 +1,6 @@
#
# Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
# Copyright (C) 2016 Furrtek
#
# This file is part of PortaPack.
#
@ -152,7 +153,9 @@ set(CPPSRC
ui_alphanum.cpp
ui_audio.cpp
ui_baseband_stats_view.cpp
ui_bht_tx.cpp
ui_channel.cpp
ui_closecall.cpp
ui_debug.cpp
ui_encoders.cpp
ui_font_fixed_8x16.cpp
@ -161,6 +164,7 @@ set(CPPSRC
ui_jammer.cpp
ui_lcr.cpp
ui_menu.cpp
ui_morse.cpp
ui_navigation.cpp
ui_numbers.cpp
ui_nuoptix.cpp
@ -176,8 +180,7 @@ set(CPPSRC
ui_textentry.cpp
ui_touch_calibration.cpp
ui_whipcalc.cpp
ui_xylos.cpp
ui_closecall.cpp
ui_whistle.cpp
# ui_loadmodule.cpp
recent_entries.cpp
receiver_model.cpp

File diff suppressed because it is too large Load Diff

View File

@ -79,10 +79,14 @@ void WFMConfig::apply() const {
audio::set_rate(audio::Rate::Hz_48000);
}
void set_ccir_data( const uint32_t samples_per_tone, const uint16_t tone_count) {
const CCIRConfigureMessage message {
samples_per_tone,
tone_count
void set_tones_data(const uint64_t bw, const uint32_t pre_silence, const uint16_t tone_count,
const bool dual_tone, const bool audio_out) {
const TonesConfigureMessage message {
(uint32_t)(262144 * bw) / 1536000,
pre_silence,
tone_count,
dual_tone,
audio_out
};
send_message(&message);
}
@ -158,14 +162,14 @@ void set_rds_data(const uint16_t message_length) {
send_message(&message);
}
void set_dtmf_data(const uint32_t bw, const uint32_t tone_length, const uint32_t pause_length) {
/*void set_dtmf_data(const uint32_t bw, const uint32_t tone_length, const uint32_t pause_length) {
const DTMFTXConfigMessage message {
bw,
tone_length,
pause_length
};
send_message(&message);
}
}*/
static bool baseband_image_running = false;

View File

@ -53,7 +53,8 @@ struct WFMConfig {
void apply() const;
};
void set_ccir_data( const uint32_t samples_per_tone, const uint16_t tone_count);
void set_tones_data(const uint64_t bw, const uint32_t pre_silence, const uint16_t tone_count,
const bool dual_tone, const bool audio_out);
void set_audiotx_data(const uint32_t divider, const uint32_t bw, const bool ctcss_enabled, const uint32_t ctcss_phase_inc);
void set_fifo_data(const int8_t * data);
void set_pwmrssi(int32_t avg, bool enabled);
@ -64,7 +65,7 @@ void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit,
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 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);
void shutdown();

View File

@ -27,6 +27,52 @@
namespace ui {
static constexpr uint8_t bitmap_stealth_data[] = {
0x00, 0x00,
0x00, 0x00,
0xE0, 0x07,
0xF8, 0x1F,
0xFC, 0x3F,
0xFC, 0x3F,
0xE4, 0x27,
0xC4, 0x23,
0xEC, 0x37,
0x7C, 0x3E,
0x7C, 0x1E,
0x2C, 0x0C,
0x0C, 0x00,
0x0C, 0x00,
0x0C, 0x00,
0x00, 0x00
};
static constexpr Bitmap bitmap_stealth {
{ 16, 16 }, bitmap_stealth_data
};
static constexpr uint8_t bitmap_speaker_data[] = {
0x00, 0x00,
0x00, 0x20,
0x00, 0x30,
0x00, 0x38,
0x00, 0x3C,
0xDC, 0x3E,
0xDC, 0x3F,
0xDC, 0x3F,
0xDC, 0x3F,
0xDC, 0x3F,
0xDC, 0x3E,
0x00, 0x3C,
0x00, 0x38,
0x00, 0x30,
0x00, 0x20,
0x00, 0x00
};
static constexpr Bitmap bitmap_speaker {
{ 16, 16 }, bitmap_speaker_data
};
static constexpr uint8_t bitmap_more_data[] = {
0x00, 0x00,
0xC0, 0x03,
@ -78,15 +124,15 @@ static constexpr uint8_t bitmap_keyboard_data[] = {
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0xF8, 0x0F,
0x88, 0x08,
0x88, 0x08,
0x88, 0x08,
0xFE, 0x3F,
0x22, 0x22,
0x22, 0x22,
0x22, 0x22,
0xFE, 0x3F,
0xF0, 0x1F,
0x10, 0x11,
0x10, 0x11,
0x10, 0x11,
0xFC, 0x7F,
0x44, 0x44,
0x44, 0x44,
0x44, 0x44,
0xFC, 0x7F,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,

View File

@ -1,4 +1,4 @@
unsigned char demofont_bin[] = {
const uint8_t demofont_bin[] = {
0xff, 0xff, 0xff, 0xff, 0xc7, 0xe4, 0xeb, 0xa3, 0xc0, 0xef, 0x82, 0x65,
0xcc, 0x6a, 0x50, 0xc8, 0x8c, 0x99, 0xa6, 0x78, 0x76, 0x8d, 0x57, 0x66,
0xa5, 0x3f, 0x52, 0x7d, 0x2d, 0x66, 0x91, 0x19, 0x46, 0x72, 0x00, 0x5e,

View File

@ -0,0 +1,221 @@
/*
* 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 __ENCODERS_H__
#define __ENCODERS_H__
namespace encoders {
#define ENC_TYPES_COUNT 14
struct encoder_def_t {
std::string name; // Encoder chip ref/name
std::string address_symbols; // "01", "01F"...
std::string data_symbols; // Same
uint16_t clk_per_symbol; // Oscillator periods per symbol
uint16_t clk_per_fragment; // Oscillator periods per symbol fragment (state)
std::vector<std::string> bit_format; // List of fragments for each symbol in previous *_symbols list order
uint8_t word_length; // Total # of symbols (not counting sync)
std::string word_format; // A for Address, D for Data, S for sync
std::string sync; // Like bit_format
uint32_t default_speed; // Default encoder clk frequency (often set by shitty resistor)
uint8_t repeat_min; // Minimum repeat count
uint16_t pause_symbols; // Length of pause between repeats in symbols
};
// Warning ! If this is changed, make sure that the UM3750 index is still good in ui_bht_tx.cpp !
const encoder_def_t encoder_defs[ENC_TYPES_COUNT] = {
// PT2260-R2
{
"2260-R2",
"01F", "01",
1024, 128,
{ "10001000", "11101110", "10001110" },
12, "AAAAAAAAAADDS",
"10000000000000000000000000000000",
150000, 2,
0
},
// PT2260-R4
{
"2260-R4",
"01F", "01",
1024, 128,
{ "10001000", "11101110", "10001110" },
12, "AAAAAAAADDDDS",
"10000000000000000000000000000000",
150000, 2,
0
},
// PT2262
{
"2262 ",
"01F", "01F",
32, 4,
{ "10001000", "11101110", "10001110" },
12, "AAAAAAAAAAAAS",
"10000000000000000000000000000000",
20000, 4,
0
},
// 16-bit ?
{
"16-bit ",
"01", "01",
32, 8,
{ "1110", "1000" }, // Opposite ?
16, "AAAAAAAAAAAAAAAAS",
"100000000000000000000",
25000, 50,
0 // ?
},
// RT1527
{
"1527 ",
"01", "01",
128, 32,
{ "1000", "1110" },
24, "SAAAAAAAAAAAAAAAAAAAADDDD",
"10000000000000000000000000000000",
100000, 4,
10 // ?
},
// HK526E
{
"526 ",
"01", "01",
24, 8,
{ "110", "100" },
12, "AAAAAAAAAAAA",
"",
20000, 4,
10 // ?
},
// HT12E
{
"12E ",
"01", "01",
3, 1,
{ "011", "001" },
12, "SAAAAAAAADDDD",
"0000000000000000000000000000000000001",
3000, 4,
10 // ?
},
// VD5026 13 bits ?
{
"5026 ",
"0123", "0123",
128, 8,
{ "1000000010000000", "1111111011111110", "1111111010000000", "1000000011111110" },
12, "SAAAAAAAAAAAA",
"000000000000000000000000000000000000000000000001", // ?
100000, 4,
10 // ?
},
// UM3750
{
"UM3750 ",
"01", "01",
96, 32,
{ "011", "001" },
12, "SAAAAAAAAAAAA",
"1",
100000, 4,
0 // ?
},
// UM3758
{
"UM3758 ",
"01F", "01",
96, 16,
{ "011011", "001001", "011001" },
18, "SAAAAAAAAAADDDDDDDD",
"1",
160000, 4,
10 // ?
},
// BA5104
{
"BA5104 ",
"01", "01",
3072, 768,
{ "1000", "1110" },
9, "SDDAAAAAAA",
"",
455000, 4,
10 // ?
},
// MC145026
{
"145026 ",
"01F", "01",
16, 1,
{ "0111111101111111", "0100000001000000", "0111111101000000" },
9, "SAAAAADDDD",
"000000000000000000",
455000, 2,
2
},
// HT6*** TODO: Add individual variations
{
"HT6*** ",
"01F", "01",
198, 33,
{ "011011", "001001", "001011" },
18, "SAAAAAAAAAAAADDDDDD",
"0000000000000000000000000000000000001011001011001",
80000, 3,
10 // ?
},
// TC9148
{
"TC9148 ",
"01", "01",
48, 12,
{ "1000", "1110", },
12, "AAAAAAAAAAAA",
"",
455000, 3,
10 // ?
}
};
} /* namespace encoders */
#endif/*__ENCODERS_H__*/

View File

@ -92,6 +92,7 @@ private:
static MessageHandlerMap message_map;
Thread* EventDispatcher::thread_event_loop = nullptr;
bool EventDispatcher::is_running = false;
bool EventDispatcher::display_sleep = false;
EventDispatcher::EventDispatcher(
ui::Widget* const top_widget,
@ -130,7 +131,7 @@ void EventDispatcher::set_display_sleep(const bool sleep) {
portapack::display.wake();
portapack::io.lcd_backlight(true);
}
display_sleep = sleep;
EventDispatcher::display_sleep = sleep;
};
eventmask_t EventDispatcher::wait() {
@ -187,8 +188,12 @@ void EventDispatcher::dispatch(const eventmask_t events) {
if( events & EVT_MASK_SWITCHES ) {
handle_switches();
}
/*if( events & EVT_MASK_LCD_FRAME_SYNC ) {
blink_timer();
}*/
if( !display_sleep ) {
if( !EventDispatcher::display_sleep ) {
if( events & EVT_MASK_LCD_FRAME_SYNC ) {
handle_lcd_frame_sync();
}
@ -286,7 +291,7 @@ void EventDispatcher::handle_switches() {
portapack::bl_tick_counter = 0;
if( display_sleep ) {
if( EventDispatcher::display_sleep ) {
// Swallow event, wake up display.
if( switches_state.any() ) {
set_display_sleep(false);
@ -308,7 +313,7 @@ void EventDispatcher::handle_switches() {
void EventDispatcher::handle_encoder() {
portapack::bl_tick_counter = 0;
if( display_sleep ) {
if( EventDispatcher::display_sleep ) {
// Swallow event, wake up display.
set_display_sleep(false);
return;

View File

@ -52,7 +52,7 @@ public:
void run();
static void request_stop();
void set_display_sleep(const bool sleep);
static void set_display_sleep(const bool sleep);
static inline void check_fifo_isr() {
if( !shared_memory.application_queue.is_empty() ) {
@ -101,7 +101,7 @@ private:
uint32_t encoder_last = 0;
static bool is_running;
bool sd_card_present = false;
bool display_sleep = false;
static bool display_sleep;
bool halt = false;
eventmask_t wait();
@ -117,6 +117,7 @@ private:
void on_touch_event(ui::TouchEvent event);
//void blink_timer();
void handle_lcd_frame_sync();
void handle_switches();
void handle_encoder();

View File

@ -23,38 +23,47 @@
// Bitmaps generated with:
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
//TEST: RDS
//TEST: Imperial in whipcalc
//TEST: Numbers
//TEST: Jammer
//TODO: Morse coder/beacon
//BUG: Xylos doesn't play last tone ?
//BUG (fixed ?): Soundboard crashes on exit if no wav files on sd card
//BUG (fixed ?): No audio in about when shown second time
//BUG: Unistroke text entry screen doesn't care about string max length parameter
//BUG (fixed ?): Unistroke text entry screen doesn't care about string max length parameter
//BUG: POCSAG RX sometimes misses the first codeword after SYNC
//BUG: Check AFSK transmit end, skips last bits ?
//BUG: RDS doesn't stop baseband when stopping tx ?
//TODO: Use ModalMessageView with yes/no for TX
//TODO: Mousejack ?
//TODO: Waveform widget ?
//TODO: Frequency manager save/load (ui_freqman)
//TODO: Move frequencykeypad from ui_receiver to ui_widget (used everywhere)
//TODO: ADS-B draw trajectory + GPS coordinates + scale, and playback
//TODO: Finish EPAR tx
//TODO: Wav visualizer
//TODO: Analog TV tx with camcorder font character generator
//TODO: Test dual tone in proc_tones and remove proc_dtmf_tx
//TODO: Morse coder for foxhunts
//TODO: Make Morse coder and Whistle use proc_tones
//TODO: RDS multiple groups (sequence)
//TODO: Frequency manager
//TODO: IQ replay
//TODO: Use ModalMessageView confirmation for TX ?
//TODO: Show address/data bit fields in OOK TX
//TODO: Scan for OOK TX
//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: Use msgpack for settings, lists... on sd card
//TODO: Frequency manager
//TODO: Replay
//Multimon-style stuff:
//TODO: AFSK receiver
//TODO: CTCSS detector
//TODO: DMR detector
//TODO: Closecall wide range fix
//TODO: SD card wiper
//TODO: GSM channel detector
//TODO: SIGFOX RX/TX
//TODO: Bodet :)
//TODO: Whistler
//TODO: LCR full message former (see norm)
//TODO: AFSK NRZI
@ -62,16 +71,15 @@
//TODO: Playdead amnesia and login
//TODO: Setup: Play dead by default ? Enable/disable ?
//TODO: Hide statusview when playing dead
//TODO: Persistent playdead !
//In case of disaster:
//BUG (fixed ?): No audio in about when shown second time
//TODO: Show MD5 mismatches for modules not found, etc...
//TODO: Module name/filename in modules.hpp to indicate requirement in case it's not found ui_loadmodule
//BUG: Description doesn't show up first time going to system>module info (UI drawn on top)
//TODO: Draw on touchscreen and transmit as spectrum paint
//TODO: Two players tic-tac-toe
//TODO: Analog TV pong game
#include "ch.h"

View File

@ -82,9 +82,17 @@ void make_4A_group(uint32_t blocks[], const uint16_t PI_code, const bool TP, con
uint32_t L = 0;
uint32_t day_code;
<<<<<<< HEAD
// 57723
if ((month == 1) || (month == 2)) L = 1;
day_code = 14956 + day + (uint32_t)((float)(year - 1900 - L) * 365.25) + uint16_t((float)((month + 1) + L * 12) * 30.6001);
=======
if ((month == 1) || (month == 2)) L = 1;
day_code = 14956 + day + (uint32_t)((float)(year - L) * 365.25) + uint16_t(((month + 1) * L * 12) * 30.6001);
>>>>>>> d402a87... RDS radiotext and time group generators
blocks[0] = PI_code;
blocks[1] = (0x4 << 12) | (0 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | ((day_code & 0x18000) >> 15);
@ -98,7 +106,11 @@ uint16_t gen_PSN(const char * psname, const RDS_flags * rds_flags) {
// 4 groups with 2 PSN characters in each
for (c = 0; c < 4; c++)
<<<<<<< HEAD
make_0B_group(&group[c][0], rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, rds_flags->TA, rds_flags->MS, rds_flags->DI, c, &psname[c * 2]);
=======
make_0B_group(&group[c][0], rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, rds_flags->TA, rds_flags->MS, rds_flags->DI, c, &psname[c * 2]);
>>>>>>> d402a87... RDS radiotext and time group generators
// Generate checkbits for each block of each group
for (c = 0; c < 4; c++) {
@ -108,7 +120,7 @@ uint16_t gen_PSN(const char * psname, const RDS_flags * rds_flags) {
group[c][3] = makeblock(group[c][3], RDS_OFFSET_D);
}
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.tx_data;
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.bb_data.data;
// Copy to tx_data for baseband
for (c = 0; c < 4 * 4; c++)
@ -150,7 +162,7 @@ uint16_t gen_RadioText(const char * text, const bool AB, const RDS_flags * rds_
group[i + 3] = makeblock(group[i + 3], RDS_OFFSET_D);
}
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.tx_data;
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.bb_data.data;
// Copy to tx_data for baseband
for (c = 0; c < (groups * 4); c++)
@ -175,7 +187,11 @@ uint16_t gen_ClockTime(const RDS_flags * rds_flags,
group[2] = makeblock(group[2], RDS_OFFSET_C);
group[3] = makeblock(group[3], RDS_OFFSET_D);
<<<<<<< HEAD
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.bb_data.data;
=======
uint32_t * tx_data_u32 = (uint32_t*)shared_memory.tx_data;
>>>>>>> d402a87... RDS radiotext and time group generators
// Copy to tx_data for baseband
for (c = 0; c < 4; c++)

View File

@ -37,11 +37,19 @@ namespace rds {
struct RDS_flags {
uint16_t PI_code;
<<<<<<< HEAD
uint8_t PTY;
uint8_t DI;
bool TP;
bool TA;
bool MS;
=======
bool TP;
uint8_t PTY;
bool TA;
bool MS;
bool DI;
>>>>>>> d402a87... RDS radiotext and time group generators
};
uint32_t makeblock(uint32_t blockdata, uint16_t offset);

View File

@ -24,6 +24,8 @@
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
using namespace portapack;
#include "radio.hpp"
@ -159,6 +161,7 @@ void ReceiverModel::enable() {
update_sampling_rate();
update_modulation();
update_headphone_volume();
led_rx.on();
}
void ReceiverModel::disable() {
@ -168,6 +171,7 @@ void ReceiverModel::disable() {
// TODO: Responsibility for enabling/disabling the radio is muddy.
// Some happens in ReceiverModel, some inside radio namespace.
radio::disable();
led_rx.off();
}
int32_t ReceiverModel::tuning_offset() {

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -24,8 +25,12 @@
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
using namespace portapack;
#include "time.hpp"
#include "event_m0.hpp"
#include "radio.hpp"
#include "audio.hpp"
@ -83,6 +88,11 @@ uint32_t TransmitterModel::baseband_oversampling() const {
return baseband_configuration.decimation_factor;
}
void TransmitterModel::on_tick_second() {
if (portapack::persistent_memory::stealth_mode())
led_tx.toggle();
}
void TransmitterModel::enable() {
enabled_ = true;
radio::set_direction(rf::Direction::Transmit);
@ -92,6 +102,13 @@ void TransmitterModel::enable() {
update_vga();
update_baseband_bandwidth();
update_baseband_configuration();
led_tx.on();
signal_token_tick_second = time::signal_tick_second += [this]() {
this->on_tick_second();
};
if (portapack::persistent_memory::stealth_mode())
EventDispatcher::set_display_sleep(true);
}
void TransmitterModel::disable() {
@ -100,6 +117,9 @@ void TransmitterModel::disable() {
// TODO: Responsibility for enabling/disabling the radio is muddy.
// Some happens in ReceiverModel, some inside radio namespace.
radio::disable();
time::signal_tick_second -= signal_token_tick_second;
led_tx.off();
}
void TransmitterModel::update_tuning_frequency() {

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -30,6 +31,7 @@
#include "rf_path.hpp"
#include "max2837.hpp"
#include "volume.hpp"
#include "signal.hpp"
class TransmitterModel {
public:
@ -70,6 +72,7 @@ private:
.sampling_rate = 3072000,
.decimation_factor = 1,
};
SignalToken signal_token_tick_second;
void update_tuning_frequency();
void update_rf_amp();
@ -78,6 +81,7 @@ private:
void update_vga();
void update_modulation();
void update_baseband_configuration();
void on_tick_second();
};
#endif/*__TRANSMITTER_MODEL_H__*/

View File

@ -20,19 +20,11 @@
* Boston, MA 02110-1301, USA.
*/
#include <ch.h>
#include "demofont.hpp"
#include "ymdata.hpp"
#include "cpld_update.hpp"
#include "portapack.hpp"
#include "audio.hpp"
#include "event_m0.hpp"
#include "ui_about.hpp"
#include "touch.hpp"
#include "sine_table.hpp"
#include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp"
@ -45,371 +37,130 @@ using namespace lpc43xx;
using namespace portapack;
namespace ui {
void AboutView::on_show() {
transmitter_model.set_tuning_frequency(1337000000); // TODO: Change
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();
baseband::set_audiotx_data(32, 50, false, 0);
//audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
}
void AboutView::render_video() {
uint8_t p, r, luma, chroma, cy;
ui::Color cc;
char ch;
float s;
// Send framebuffer to LCD. Gotta go fast !
display.render_box({30, 112}, {180, 72}, framebuffer);
// Clear framebuffer to black
memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color));
// Drum hit palette animation
if (drum > 1) drum--;
// Render copper bars from Y buffer
for (p = 0; p < 72; p++) {
luma = copperbuffer[p] & 0x0F; // 0 is transparent
if (luma) {
chroma = copperbuffer[p]>>4;
cc = ui::Color(std::min((coppercolor[chroma][0]/luma)*drum,255), std::min((coppercolor[chroma][1]/luma)*drum,255), std::min((coppercolor[chroma][2]/luma)*drum,255));
for (r = 0; r < 180; r++)
framebuffer[(p*180)+r] = cc;
}
}
// Scroll in/out state machine
if (anim_state == 0) {
// Scroll in
if (ofy < 8) {
ofy++;
anim_state = 0;
} else {
anim_state = 1;
}
if (ofx < (int16_t)(180 - (strlen(credits[credits_index].name) * 16) - 8)) {
ofx += 8;
anim_state = 0;
}
} else if (anim_state == 1) {
// Just wait
if (credits_timer == (30*3)) {
credits_timer = 0;
anim_state = 2;
} else {
credits_timer++;
}
} else {
// Scroll out
if (credits[credits_index].change == true) {
if (ofy > -24) {
ofy--;
anim_state = 2;
} else {
anim_state = 0;
}
} else {
anim_state = 0;
}
if (ofx < 180) {
ofx += 8;
anim_state = 2;
}
// Switch to next text
if (anim_state == 0) {
if (credits_index == 9)
credits_index = 0;
else
credits_index++;
ofx = -(strlen(credits[credits_index].name) * 16) - 16;
}
}
// Sine text ("role")
p = 0;
while ((ch = credits[credits_index].role[p])) {
draw_demoglyph({(ui::Coord)(8+(p*16)), (ui::Coord)(ofy+(sine_table_f32[((p*16)+(phase>>5))&0xFF] * 8))}, ch, paletteA);
p++;
}
// Scroll text (name)
p = 0;
while ((ch = credits[credits_index].name[p])) {
draw_demoglyph({(ui::Coord)(ofx+(p*16)), 56}, ch, paletteB);
p++;
}
// Clear bars Y buffer
memset(copperbuffer, 0, 72);
// Render bars to Y buffer
for (p = 0; p < 5; p++) {
cy = copperbars[p];
for (r = 0; r < 16; r++)
copperbuffer[cy+r] = copperluma[r] + (p<<4);
}
// Animate bars positions
for (p = 0; p < 5; p++) {
s = sine_table_f32[((p*32)+(phase/24))&0xFF];
s += sine_table_f32[((p*16)+(phase/35))&0xFF];
copperbars[p] = 28+(uint8_t)(s * 14);
}
phase += 128;
}
void AboutView::draw_demoglyph(ui::Point p, char ch, ui::Color * pal) {
uint8_t x, y, c, cl, cr;
uint16_t che;
int16_t lbx, il;
// Map ASCII to font bitmap
if ((ch >= 32) || (ch < 96))
che = char_map[ch-32];
else
che = 0xFF;
if (che < 0xFF) {
che = (che * 128) + 48; // Start in bitmap
il = (180 * p.y) + p.x; // Start il framebuffer
for (y = 0; y < 16; y++) {
if (p.y + y >= 72) break; // Over bottom of framebuffer, abort
if (p.y + y >= 0) {
for (x = 0; x < 8; x++) {
c = demofont_bin[x+(y*8)+che]; // Split byte in 2 4BPP pixels
cl = c >> 4;
cr = c & 0x0F;
lbx = p.x + (x*2);
if (cl && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cl];
lbx++;
il++;
if (cr && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cr];
il++;
}
il += 180-16;
} else {
il += 180;
}
}
}
}
void AboutView::render_audio() {
uint8_t i, ymdata;
uint16_t ym_render_cnt;
// This is heavily inspired by MAME's ay8910.cpp and the YM2149's datasheet
// Render 1024 music samples
for (ym_render_cnt = 0; ym_render_cnt < 1024; ym_render_cnt++) {
// Update registers at 48000/960 = 50Hz
if (ym_sample_cnt == 0) {
// "Decompress" on the fly and update YM registers
for (i = 0; i < 14; i++) {
if (!ym_regs[i].cnt) {
// New run
ymdata = ymdata_bin[ym_regs[i].ptr++];
ym_regs[i].cnt = ymdata & 0x7F;
if (ymdata & 0x80) {
ym_regs[i].same = true;
ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++];
} else {
ym_regs[i].same = false;
}
// Detect drum on channel B
if (i == 3)
if (ym_regs[3].value > 2) drum = 4;
}
if (ym_regs[i].same == false) {
ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++];
if (i == 13) {
// Update envelope attributes
ym_env_att = (ym_regs[13].value & 4) ? 0x1F : 0x00;
if (!(ym_regs[13].value & 8)) {
ym_env_hold = 1;
ym_env_alt = ym_env_att;
} else {
ym_env_hold = ym_regs[13].value & 1;
ym_env_alt = ym_regs[13].value & 2;
}
// Reset envelope counter
ym_env_step = 0x1F;
ym_env_holding = 0;
ym_env_vol = (ym_env_step ^ ym_env_att);
}
}
ym_regs[i].cnt--;
}
ym_frame++;
}
// Square wave oscillators
// 2457600/16/48000 = 3.2, but 4 sounds better than 3...
for (i = 0; i < 3; i++) {
ym_osc_cnt[i] += 4;
if (ym_osc_cnt[i] >= (ym_regs[i*2].value | ((ym_regs[(i*2)+1].value & 0x0f) << 8))) {
ym_osc_cnt[i] = 0;
ym_osc_out[i] ^= 1;
}
}
// Noise generator
ym_noise_cnt += 4;
if (ym_noise_cnt >= ((ym_regs[6].value & 0x1F) * 2)) {
ym_noise_cnt = 0;
ym_rng ^= (((ym_rng & 1) ^ ((ym_rng >> 3) & 1)) << 17);
ym_rng >>= 1;
}
// Mix tones and noise
for (i = 0; i < 3; i++)
ym_ch[i] = (ym_osc_out[i] | ((ym_regs[7].value >> i) & 1)) & ((ym_rng & 1) | ((ym_regs[7].value >> (i + 3)) & 1));
// Envelope generator
if (!ym_env_holding) {
ym_env_cnt += 8;
if (ym_env_cnt >= (ym_regs[11].value | (ym_regs[12].value<<8))) {
ym_env_cnt = 0;
ym_env_step--;
if (ym_env_step < 0) {
if (ym_env_hold) {
if (ym_env_alt)
ym_env_att ^= 0x1F;
ym_env_holding = 1;
ym_env_step = 0;
} else {
if (ym_env_alt && (ym_env_step & 0x20))
ym_env_att ^= 0x1F;
ym_env_step &= 0x1F;
}
}
}
}
ym_env_vol = (ym_env_step ^ ym_env_att);
ym_out = 0;
for (i = 0; i < 3; i++) {
if (ym_regs[i + 8].value & 0x10) {
// Envelope mode
ym_out += (ym_ch[i] ? ym_env_vol : 0);
} else {
// Fixed mode
ym_out += (ym_ch[i] ? (ym_regs[i + 8].value & 0x0F) : 0);
}
}
ym_buffer[ym_render_cnt] = (ym_out * 2) - 45;
if (ym_sample_cnt < 960) {
ym_sample_cnt++;
} else {
ym_sample_cnt = 0;
}
// Loop
if (ym_frame == ym_frames) ym_init();
}
}
void AboutView::update() {
if (framebuffer) {
// Update 1 out of 2 frames, 60Hz is very laggy
if (refresh_cnt & 1) render_video();
refresh_cnt++;
}
size_t c;
int32_t n;
std::string text;
Coord y_val, x_pos = 0;
uint32_t flag;
// Slowly increase volume to avoid jumpscare
if (headphone_vol < (70 << 2)) {
audio::headphone::set_volume(volume_t::decibel((headphone_vol/4) - 99) + audio::headphone::volume_range().max);
headphone_vol++;
}
}
if (scroll & 1) {
if (!((scroll >> 1) & 15)) {
if (line_feed) {
line_feed = false;
} else {
// Find a free text widget
for (c = 0; c < 10; c++)
if (text_line[c].screen_pos().y >= 200) break;
if (c < 10) {
flag = credits[credits_index].flag & 0x3F;
line_feed = (credits[credits_index].flag & 0x40) ? true : false;
if (flag == SECTION) {
if (!second) {
text = credits[credits_index].role;
if (credits[credits_index].name != "")
second = true;
else {
credits_index++;
line_feed = true;
}
} else {
text = credits[credits_index].name;
second = false;
line_feed = true;
credits_index++;
}
x_pos = (240 - (text.size() * 8)) / 2;
} else if (flag == TITLE) {
text = credits[credits_index].role;
x_pos = 120 - (text.size() * 8);
if (credits[credits_index].name != "")
text += " " + credits[credits_index].name;
credits_index++;
} else if (flag == MEMBER) {
if (!second) {
text = credits[credits_index].role;
if (credits[credits_index].name != "")
second = true;
else
credits_index++;
} else {
text = credits[credits_index].name;
second = false;
credits_index++;
}
x_pos = 136;
}
if (!(flag & 0x80)) {
text_line[c].set_parent_rect({{ x_pos, 200 - 16 }, { (Dim)text.size() * 8, 17 }});
text_line[c].set(text);
text_line[c].hidden(false);
}
}
}
}
void AboutView::ym_init() {
uint8_t reg;
for (reg = 0; reg < 14; reg++) {
ym_regs[reg].cnt = 0;
// Pick up start pointers for each YM registers RLE blocks
ym_regs[reg].ptr = ((uint16_t)(ymdata_bin[(reg*2)+3])<<8) + ymdata_bin[(reg*2)+2];
ym_regs[reg].same = false; // Useless ?
ym_regs[reg].value = 0; // Useless ?
// Scroll text lines
for (c = 0; c < 10; c++) {
y_val = text_line[c].screen_pos().y - 16;
if (y_val < 32) {
text_line[c].set_parent_rect({{ text_line[c].screen_pos().x, 200 }, { text_line[c].size() }});
text_line[c].hidden(true);
} else {
if (y_val < 200) {
text_line[c].set_parent_rect({{ text_line[c].screen_pos().x, y_val - 1 }, { text_line[c].size() }});
n = (y_val - 32) >> 2;
if (n > 19)
n = (38 - n);
else
n -= 3;
if (n > 3) n = 3;
if (n < 0) n = 0;
text_line[c].set_style(&styles[n]);
}
}
}
}
ym_frame = 0;
scroll++;
}
AboutView::AboutView(
NavigationView& nav
)
{
uint8_t p, c;
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
//uint8_t p, c;
add_children({ {
&text_title,
&text_firmware,
&text_cpld_hackrf,
&text_cpld_hackrf_status,
&button_ok,
} });
for (auto& text : text_line) {
text.set("");
text.set_parent_rect({
static_cast<Coord>(0),
static_cast<Coord>(200),
0, 0
});
add_child(&text);
}
if( cpld_hackrf_verify_eeprom() ) {
text_cpld_hackrf_status.set(" OK");
} else {
text_cpld_hackrf_status.set("BAD");
}
// Politely ask for about 26kB
framebuffer = (ui::Color *)chHeapAlloc(0x0, 180 * 72 * sizeof(ui::Color));
if (framebuffer) {
memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color));
// Copy original font palette
c = 0;
for (p = 0; p < 48; p+=3)
paletteA[c++] = ui::Color(demofont_bin[p], demofont_bin[p+1], demofont_bin[p+2]);
// Increase red in another one
c = 0;
for (p = 0; p < 48; p+=3)
paletteB[c++] = ui::Color(std::min(demofont_bin[p]+64, 255), demofont_bin[p+1], demofont_bin[p+2]);
}
// Init YM synth
ym_frames = ((uint16_t)(ymdata_bin[1])<<8) + ymdata_bin[0];
ym_init();
button_ok.on_select = [this,&nav](Button&){
if (framebuffer) chHeapFree(framebuffer); // Do NOT forget this
nav.pop();
};
}
AboutView::~AboutView() {
transmitter_model.disable();
baseband::shutdown();
}
void AboutView::focus() {
button_ok.focus();
}

View File

@ -26,8 +26,7 @@
#include "ui_widget.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "transmitter_model.hpp"
#include "baseband_api.hpp"
#include "ui_font_fixed_8x16.hpp"
#include <cstdint>
@ -36,103 +35,73 @@ namespace ui {
class AboutView : public View {
public:
AboutView(NavigationView& nav);
~AboutView();
void on_show() override;
void focus() override;
std::string title() const override { return "About"; };
private:
void ym_init();
void update();
void render_video();
void render_audio();
void draw_demoglyph(ui::Point p, char ch, ui::Color * pal);
uint16_t debug_cnt = 0;
typedef struct ymreg_t {
uint8_t value;
uint8_t cnt;
uint16_t ptr;
bool same;
} ymreg_t;
uint16_t headphone_vol = 5 << 2;
ymreg_t ym_regs[14];
uint16_t ym_frames;
uint16_t ym_frame;
uint8_t drum = 0;
uint16_t ym_osc_cnt[3];
uint32_t ym_rng = 1;
uint16_t ym_noise_cnt;
uint8_t ym_env_att, ym_env_hold, ym_env_alt, ym_env_holding, ym_env_vol;
int8_t ym_env_step;
uint16_t ym_env_cnt;
uint8_t ym_osc_out[3];
uint8_t ym_ch[3];
uint8_t ym_out;
uint16_t ym_sample_cnt = 0;
int8_t ym_buffer[1024];
uint8_t refresh_cnt;
ui::Color paletteA[16];
ui::Color paletteB[16];
ui::Color * framebuffer;
uint32_t phase = 0;
uint8_t copperbars[5] = { 0 };
uint8_t copperbuffer[72] = { 0 };
uint8_t anim_state = 0;
uint8_t credits_index = 0;
uint16_t credits_timer = 0;
uint32_t scroll = 0;
bool second = false;
bool line_feed = false;
int16_t ofx = -180, ofy = -24;
const uint8_t char_map[64] = { 0xFF, 27, 46, 0xFF, 0xFF, 0xFF, 28, 45,
58, 59, 0xFF, 43, 40, 57, 26, 42,
29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 41, 0xFF, 0xFF, 0xFF, 0xFF, 44,
0xFF, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
const uint8_t copperluma[16] = { 8,7,6,5,4,3,2,1,1,2,3,4,5,6,7,8 };
const uint8_t coppercolor[5][3] = { { 255,0,0 },
{ 0,255,0 },
{ 0,0,255 },
{ 255,0,255 },
{ 255,255,0 } };
Style styles[4] = { style_black, style_dark_grey, style_light_grey, style_white };
static constexpr Style style_white {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color(255, 255, 255)
};
static constexpr Style style_light_grey {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color(170, 170, 170)
};
static constexpr Style style_dark_grey {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color(85, 85, 85)
};
static constexpr Style style_black {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color(0, 0, 0)
};
enum flags {
SECTION = 0,
MEMBER = 1,
MEMBER_LF = 0x41,
TITLE = 2,
TITLE_LF = 0x42,
END = 0x80
};
typedef struct credits_t {
char role[12];
char name[12];
bool change;
std::string role;
std::string name;
flags flag;
} credits_t;
// 0123456789A 0123456789A
const credits_t credits[10] = { {"GURUS", "J. BOONE", false},
{"GURUS", "M. OSSMANN", true},
{"BUGS", "FURRTEK", true},
{"RDS WAVE", "C. JACQUET", true},
{"POCSAG RX", "T. SAILER", false},
{"POCSAG RX", "E. OENAL", true},
{"XYLOS DATA", "CLX", true},
{"GREETS TO", "SIGMOUNTE", false},
{"GREETS TO", "WINDYOONA", true},
{"THIS MUSIC", "BIG ALEC", true}
const credits_t credits[13] = { {"Portapack|HAVOC", "Git hash " GIT_REVISION, SECTION},
{"Gurus", "J. Boone", TITLE},
{"M. Ossmann", "", MEMBER_LF},
{"Immaturity", "Furrtek", TITLE_LF},
{"POCSAG rx", "T. Sailer", TITLE},
{"E. Oenal", "", MEMBER_LF},
{"RDS waveform", "C. Jacquet", TITLE_LF},
{"Xy. infos", "cLx", TITLE_LF},
{"Thanks", "", SECTION},
{"Rainer Matla", "Keld Norman", TITLE},
{"Sigmounte", "Waax", TITLE},
{"Windyoona", "Channels", TITLE_LF},
{"", "MMXVI", END}
};
Text text_title {
{ 100, 32, 40, 16 },
"About",
};
Text text_firmware {
{ 0, 236, 240, 16 },
"Git Commit Hash " GIT_REVISION,
};
std::array<Text, 10> text_line;
Text text_cpld_hackrf {
{ 0, 252, 11*8, 16 },
@ -156,17 +125,6 @@ private:
}
};
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->render_audio();
baseband::set_fifo_data(ym_buffer);
}
}
};
};
} /* namespace ui */

View File

@ -0,0 +1,418 @@
/*
* Copyright (C) 2015 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 <ch.h>
#include "demofont.hpp"
#include "ymdata.hpp"
#include "cpld_update.hpp"
#include "portapack.hpp"
#include "audio.hpp"
#include "event_m0.hpp"
#include "ui_about.hpp"
#include "touch.hpp"
#include "sine_table.hpp"
#include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp"
#include "lpc43xx_cpp.hpp"
#include <math.h>
#include <cstring>
using namespace lpc43xx;
using namespace portapack;
namespace ui {
void AboutView::on_show() {
transmitter_model.set_tuning_frequency(1337000000); // TODO: Change
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();
baseband::set_audiotx_data(32, 50, false, 0);
//audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
}
void AboutView::render_video() {
uint8_t p, r, luma, chroma, cy;
ui::Color cc;
char ch;
float s;
// Send framebuffer to LCD. Gotta go fast !
display.render_box({30, 112}, {180, 72}, framebuffer);
// Clear framebuffer to black
memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color));
// Drum hit palette animation
if (drum > 1) drum--;
// Render copper bars from Y buffer
for (p = 0; p < 72; p++) {
luma = copperbuffer[p] & 0x0F; // 0 is transparent
if (luma) {
chroma = copperbuffer[p]>>4;
cc = ui::Color(std::min((coppercolor[chroma][0]/luma)*drum,255), std::min((coppercolor[chroma][1]/luma)*drum,255), std::min((coppercolor[chroma][2]/luma)*drum,255));
for (r = 0; r < 180; r++)
framebuffer[(p*180)+r] = cc;
}
}
// Scroll in/out state machine
if (anim_state == 0) {
// Scroll in
if (ofy < 8) {
ofy++;
anim_state = 0;
} else {
anim_state = 1;
}
if (ofx < (int16_t)(180 - (strlen(credits[credits_index].name) * 16) - 8)) {
ofx += 8;
anim_state = 0;
}
} else if (anim_state == 1) {
// Just wait
if (credits_timer == (30*3)) {
credits_timer = 0;
anim_state = 2;
} else {
credits_timer++;
}
} else {
// Scroll out
if (credits[credits_index].change == true) {
if (ofy > -24) {
ofy--;
anim_state = 2;
} else {
anim_state = 0;
}
} else {
anim_state = 0;
}
if (ofx < 180) {
ofx += 8;
anim_state = 2;
}
// Switch to next text
if (anim_state == 0) {
if (credits_index == 9)
credits_index = 0;
else
credits_index++;
ofx = -(strlen(credits[credits_index].name) * 16) - 16;
}
}
// Sine text ("role")
p = 0;
while ((ch = credits[credits_index].role[p])) {
draw_demoglyph({(ui::Coord)(8+(p*16)), (ui::Coord)(ofy+(sine_table_f32[((p*16)+(phase>>5))&0xFF] * 8))}, ch, paletteA);
p++;
}
// Scroll text (name)
p = 0;
while ((ch = credits[credits_index].name[p])) {
draw_demoglyph({(ui::Coord)(ofx+(p*16)), 56}, ch, paletteB);
p++;
}
// Clear bars Y buffer
memset(copperbuffer, 0, 72);
// Render bars to Y buffer
for (p = 0; p < 5; p++) {
cy = copperbars[p];
for (r = 0; r < 16; r++)
copperbuffer[cy+r] = copperluma[r] + (p<<4);
}
// Animate bars positions
for (p = 0; p < 5; p++) {
s = sine_table_f32[((p*32)+(phase/24))&0xFF];
s += sine_table_f32[((p*16)+(phase/35))&0xFF];
copperbars[p] = 28+(uint8_t)(s * 14);
}
phase += 128;
}
void AboutView::draw_demoglyph(ui::Point p, char ch, ui::Color * pal) {
uint8_t x, y, c, cl, cr;
uint16_t che;
int16_t lbx, il;
// Map ASCII to font bitmap
if ((ch >= 32) || (ch < 96))
che = char_map[ch-32];
else
che = 0xFF;
if (che < 0xFF) {
che = (che * 128) + 48; // Start in bitmap
il = (180 * p.y) + p.x; // Start il framebuffer
for (y = 0; y < 16; y++) {
if (p.y + y >= 72) break; // Over bottom of framebuffer, abort
if (p.y + y >= 0) {
for (x = 0; x < 8; x++) {
c = demofont_bin[x+(y*8)+che]; // Split byte in 2 4BPP pixels
cl = c >> 4;
cr = c & 0x0F;
lbx = p.x + (x*2);
if (cl && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cl];
lbx++;
il++;
if (cr && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cr];
il++;
}
il += 180-16;
} else {
il += 180;
}
}
}
}
void AboutView::render_audio() {
uint8_t i, ymdata;
uint16_t ym_render_cnt;
// This is heavily inspired by MAME's ay8910.cpp and the YM2149's datasheet
// Render 1024 music samples
for (ym_render_cnt = 0; ym_render_cnt < 1024; ym_render_cnt++) {
// Update registers at 48000/960 = 50Hz
if (ym_sample_cnt == 0) {
// "Decompress" on the fly and update YM registers
for (i = 0; i < 14; i++) {
if (!ym_regs[i].cnt) {
// New run
ymdata = ymdata_bin[ym_regs[i].ptr++];
ym_regs[i].cnt = ymdata & 0x7F;
if (ymdata & 0x80) {
ym_regs[i].same = true;
ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++];
} else {
ym_regs[i].same = false;
}
// Detect drum on channel B
if (i == 3)
if (ym_regs[3].value > 2) drum = 4;
}
if (ym_regs[i].same == false) {
ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++];
if (i == 13) {
// Update envelope attributes
ym_env_att = (ym_regs[13].value & 4) ? 0x1F : 0x00;
if (!(ym_regs[13].value & 8)) {
ym_env_hold = 1;
ym_env_alt = ym_env_att;
} else {
ym_env_hold = ym_regs[13].value & 1;
ym_env_alt = ym_regs[13].value & 2;
}
// Reset envelope counter
ym_env_step = 0x1F;
ym_env_holding = 0;
ym_env_vol = (ym_env_step ^ ym_env_att);
}
}
ym_regs[i].cnt--;
}
ym_frame++;
}
// Square wave oscillators
// 2457600/16/48000 = 3.2, but 4 sounds better than 3...
for (i = 0; i < 3; i++) {
ym_osc_cnt[i] += 4;
if (ym_osc_cnt[i] >= (ym_regs[i*2].value | ((ym_regs[(i*2)+1].value & 0x0f) << 8))) {
ym_osc_cnt[i] = 0;
ym_osc_out[i] ^= 1;
}
}
// Noise generator
ym_noise_cnt += 4;
if (ym_noise_cnt >= ((ym_regs[6].value & 0x1F) * 2)) {
ym_noise_cnt = 0;
ym_rng ^= (((ym_rng & 1) ^ ((ym_rng >> 3) & 1)) << 17);
ym_rng >>= 1;
}
// Mix tones and noise
for (i = 0; i < 3; i++)
ym_ch[i] = (ym_osc_out[i] | ((ym_regs[7].value >> i) & 1)) & ((ym_rng & 1) | ((ym_regs[7].value >> (i + 3)) & 1));
// Envelope generator
if (!ym_env_holding) {
ym_env_cnt += 8;
if (ym_env_cnt >= (ym_regs[11].value | (ym_regs[12].value<<8))) {
ym_env_cnt = 0;
ym_env_step--;
if (ym_env_step < 0) {
if (ym_env_hold) {
if (ym_env_alt)
ym_env_att ^= 0x1F;
ym_env_holding = 1;
ym_env_step = 0;
} else {
if (ym_env_alt && (ym_env_step & 0x20))
ym_env_att ^= 0x1F;
ym_env_step &= 0x1F;
}
}
}
}
ym_env_vol = (ym_env_step ^ ym_env_att);
ym_out = 0;
for (i = 0; i < 3; i++) {
if (ym_regs[i + 8].value & 0x10) {
// Envelope mode
ym_out += (ym_ch[i] ? ym_env_vol : 0);
} else {
// Fixed mode
ym_out += (ym_ch[i] ? (ym_regs[i + 8].value & 0x0F) : 0);
}
}
ym_buffer[ym_render_cnt] = (ym_out * 2) - 45;
if (ym_sample_cnt < 960) {
ym_sample_cnt++;
} else {
ym_sample_cnt = 0;
}
// Loop
if (ym_frame == ym_frames) ym_init();
}
}
void AboutView::update() {
if (framebuffer) {
// Update 1 out of 2 frames, 60Hz is very laggy
if (refresh_cnt & 1) render_video();
refresh_cnt++;
}
// Slowly increase volume to avoid jumpscare
if (headphone_vol < (70 << 2)) {
audio::headphone::set_volume(volume_t::decibel((headphone_vol/4) - 99) + audio::headphone::volume_range().max);
headphone_vol++;
}
}
void AboutView::ym_init() {
uint8_t reg;
for (reg = 0; reg < 14; reg++) {
ym_regs[reg].cnt = 0;
// Pick up start pointers for each YM registers RLE blocks
ym_regs[reg].ptr = ((uint16_t)(ymdata_bin[(reg*2)+3])<<8) + ymdata_bin[(reg*2)+2];
ym_regs[reg].same = false; // Useless ?
ym_regs[reg].value = 0; // Useless ?
}
ym_frame = 0;
}
AboutView::AboutView(
NavigationView& nav
)
{
uint8_t p, c;
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
add_children({ {
&text_title,
&text_firmware,
&text_cpld_hackrf,
&text_cpld_hackrf_status,
&button_ok,
} });
if( cpld_hackrf_verify_eeprom() ) {
text_cpld_hackrf_status.set(" OK");
} else {
text_cpld_hackrf_status.set("BAD");
}
// Politely ask for about 26kB
framebuffer = (ui::Color *)chHeapAlloc(0x0, 180 * 72 * sizeof(ui::Color));
if (framebuffer) {
memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color));
// Copy original font palette
c = 0;
for (p = 0; p < 48; p+=3)
paletteA[c++] = ui::Color(demofont_bin[p], demofont_bin[p+1], demofont_bin[p+2]);
// Increase red in another one
c = 0;
for (p = 0; p < 48; p+=3)
paletteB[c++] = ui::Color(std::min(demofont_bin[p]+64, 255), demofont_bin[p+1], demofont_bin[p+2]);
}
// Init YM synth
ym_frames = ((uint16_t)(ymdata_bin[1])<<8) + ymdata_bin[0];
ym_init();
button_ok.on_select = [this,&nav](Button&){
if (framebuffer) chHeapFree(framebuffer); // Do NOT forget this
nav.pop();
};
}
AboutView::~AboutView() {
transmitter_model.disable();
baseband::shutdown();
}
void AboutView::focus() {
button_ok.focus();
}
} /* namespace ui */

View File

@ -0,0 +1,174 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_ABOUT_H__
#define __UI_ABOUT_H__
#include "ui_widget.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "transmitter_model.hpp"
#include "baseband_api.hpp"
#include <cstdint>
namespace ui {
class AboutView : public View {
public:
AboutView(NavigationView& nav);
~AboutView();
void on_show() override;
void focus() override;
private:
void ym_init();
void update();
void render_video();
void render_audio();
void draw_demoglyph(ui::Point p, char ch, ui::Color * pal);
uint16_t debug_cnt = 0;
typedef struct ymreg_t {
uint8_t value;
uint8_t cnt;
uint16_t ptr;
bool same;
} ymreg_t;
uint16_t headphone_vol = 5 << 2;
ymreg_t ym_regs[14];
uint16_t ym_frames;
uint16_t ym_frame;
uint8_t drum = 0;
uint16_t ym_osc_cnt[3];
uint32_t ym_rng = 1;
uint16_t ym_noise_cnt;
uint8_t ym_env_att, ym_env_hold, ym_env_alt, ym_env_holding, ym_env_vol;
int8_t ym_env_step;
uint16_t ym_env_cnt;
uint8_t ym_osc_out[3];
uint8_t ym_ch[3];
uint8_t ym_out;
uint16_t ym_sample_cnt = 0;
int8_t ym_buffer[1024];
uint8_t refresh_cnt;
ui::Color paletteA[16];
ui::Color paletteB[16];
ui::Color * framebuffer;
uint32_t phase = 0;
uint8_t copperbars[5] = { 0 };
uint8_t copperbuffer[72] = { 0 };
uint8_t anim_state = 0;
uint8_t credits_index = 0;
uint16_t credits_timer = 0;
int16_t ofx = -180, ofy = -24;
const uint8_t char_map[64] = { 0xFF, 27, 46, 0xFF, 0xFF, 0xFF, 28, 45,
58, 59, 0xFF, 43, 40, 57, 26, 42,
29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 41, 0xFF, 0xFF, 0xFF, 0xFF, 44,
0xFF, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
const uint8_t copperluma[16] = { 8,7,6,5,4,3,2,1,1,2,3,4,5,6,7,8 };
const uint8_t coppercolor[5][3] = { { 255,0,0 },
{ 0,255,0 },
{ 0,0,255 },
{ 255,0,255 },
{ 255,255,0 } };
typedef struct credits_t {
char role[12];
char name[12];
bool change;
} credits_t;
// 0123456789A 0123456789A
const credits_t credits[10] = { {"GURUS", "J. BOONE", false},
{"GURUS", "M. OSSMANN", true},
{"BUGS", "FURRTEK", true},
{"RDS WAVE", "C. JACQUET", true},
{"POCSAG RX", "T. SAILER", false},
{"POCSAG RX", "E. OENAL", true},
{"XYLOS DATA", "CLX", true},
{"GREETS TO", "SIGMOUNTE", false},
{"GREETS TO", "WINDYOONA", true},
{"THIS MUSIC", "BIG ALEC", true}
};
Text text_title {
{ 100, 32, 40, 16 },
"About",
};
Text text_firmware {
{ 0, 236, 240, 16 },
"Git Commit Hash " GIT_REVISION,
};
Text text_cpld_hackrf {
{ 0, 252, 11*8, 16 },
"HackRF CPLD",
};
Text text_cpld_hackrf_status {
{ 240 - 3*8, 252, 3*8, 16 },
"???"
};
Button button_ok {
{ 72, 272, 96, 24 },
"OK"
};
MessageHandlerRegistration message_handler_update {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->update();
}
};
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->render_audio();
baseband::set_fifo_data(ym_buffer);
}
}
};
};
} /* namespace ui */
#endif/*__UI_ABOUT_H__*/

View File

@ -95,7 +95,7 @@ void ADSBTxView::start_tx() {
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
memcpy(shared_memory.tx_data, adsb_bin, 112);
memcpy(shared_memory.bb_data.data, adsb_bin, 112);
baseband::set_adsb();
}

View File

@ -163,7 +163,7 @@ private:
Message::ID::TXDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
this->on_txdone(message.n);
this->on_txdone(message.progress);
}
};
};

View File

@ -22,12 +22,7 @@
#include "ui_alphanum.hpp"
#include "ch.h"
#include "ff.h"
#include "portapack.hpp"
#include "radio.hpp"
#include "hackrf_hal.hpp"
#include "portapack_shared_memory.hpp"
@ -45,9 +40,9 @@ void AlphanumView::paint(Painter& painter) {
AlphanumView::AlphanumView(
NavigationView& nav,
char txt[],
uint8_t max_len
) {
_max_len = max_len;
size_t max_length
) : _max_length(max_length)
{
_lowercase = false;
size_t n;
@ -64,14 +59,19 @@ AlphanumView::AlphanumView(
};
txtidx = strlen(txt);
memcpy(txtinput, txt, max_len + 1);
memcpy(txtinput, txt, _max_length + 1);
n = txtidx;
while (n && (txtinput[n - 1] == ' ')) {
txtinput[--n] = 0;
txtidx--;
}
add_child(&text_input);
add_children({ {
&text_input,
&button_lowercase,
&raw_char,
&button_ok
} });
const auto button_fn = [this](Button& button) {
this->on_button(button);
@ -81,9 +81,9 @@ AlphanumView::AlphanumView(
for (auto& button : buttons) {
button.on_select = button_fn;
button.set_parent_rect({
static_cast<Coord>((n % 5) * button_w),
static_cast<Coord>((n / 5) * button_h + 24),
button_w, button_h
static_cast<Coord>((n % 5) * (240 / 5)),
static_cast<Coord>((n / 5) * 28 + 24),
240 / 5, 28
});
if ((n < 10) || (n == 39))
button.set_style(&style_num);
@ -92,31 +92,28 @@ AlphanumView::AlphanumView(
add_child(&button);
n++;
}
set_uppercase();
set_keys(keys_upper);
add_child(&button_lowercase);
button_lowercase.on_select = [this, &nav, max_len](Button&) {
button_lowercase.on_select = [this, &nav](Button&) {
if (_lowercase == true) {
_lowercase = false;
button_lowercase.set_text("UC");
set_uppercase();
set_keys(keys_upper);
} else {
_lowercase = true;
button_lowercase.set_text("LC");
set_lowercase();
set_keys(keys_lower);
}
};
add_child(&raw_char);
raw_char.set_value(0x30);
raw_char.on_select = [this, &nav, txt, max_len](NumberField&) {
raw_char.set_value('0');
raw_char.on_select = [this, &nav](NumberField&) {
char_add(raw_char.value());
update_text();
};
add_child(&button_done);
button_done.on_select = [this, &nav, txt, max_len](Button&) {
memcpy(txt, txtinput, max_len + 1);
button_ok.on_select = [this, &nav, txt, max_length](Button&) {
memcpy(txt, txtinput, max_length + 1);
if (on_changed) on_changed(this->value());
nav.pop();
};
@ -140,29 +137,12 @@ void AlphanumView::move_cursor() {
);
}
void AlphanumView::set_uppercase() {
const char* const key_caps = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ. !<";
void AlphanumView::set_keys(const char * const key_list) {
size_t n = 0;
for(auto& button : buttons) {
//add_child(&button);
for (auto& button : buttons) {
const std::string label {
key_caps[n]
};
button.set_text(label);
n++;
}
}
void AlphanumView::set_lowercase() {
const char* const key_caps = "0123456789abcdefghijklmnopqrstuvwxyz:=?<";
size_t n = 0;
for(auto& button : buttons) {
//add_child(&button);
const std::string label {
key_caps[n]
key_list[n]
};
button.set_text(label);
n++;
@ -170,7 +150,7 @@ void AlphanumView::set_lowercase() {
}
void AlphanumView::focus() {
button_done.focus();
button_ok.focus();
}
char * AlphanumView::value() {
@ -180,30 +160,31 @@ char * AlphanumView::value() {
void AlphanumView::on_button(Button& button) {
const auto s = button.text();
if( s == "<" ) {
if (s == "<")
char_delete();
} else {
else
char_add(s[0]);
}
update_text();
}
void AlphanumView::char_add(const char c) {
if (txtidx < _max_len) {
txtinput[txtidx] = c;
txtidx++;
}
if (txtidx >= _max_length) return;
txtinput[txtidx] = c;
txtidx++;
}
void AlphanumView::char_delete() {
if (txtidx) {
txtidx--;
txtinput[txtidx] = 0;
}
if (!txtidx) return;
txtidx--;
txtinput[txtidx] = 0;
}
void AlphanumView::update_text() {
text_input.set(std::string(txtinput) + std::string(_max_len - strlen(txtinput), ' '));
text_input.set(std::string(txtinput) + std::string(_max_length - strlen(txtinput), ' '));
move_cursor();
}

View File

@ -29,12 +29,6 @@
#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"
namespace ui {
@ -42,26 +36,30 @@ class AlphanumView : public View {
public:
std::function<void(char *)> on_changed;
AlphanumView(NavigationView& nav, char txt[], uint8_t max_len);
AlphanumView(NavigationView& nav, char txt[], size_t max_length);
void paint(Painter& painter) override;
void focus() override;
char * value();
std::string title() const override { return "Text entry"; };
private:
uint8_t _max_len;
const char * const keys_upper = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ. !<";
const char * const keys_lower = "0123456789abcdefghijklmnopqrstuvwxyz:=?<";
size_t _max_length;
uint8_t txtidx;
bool _lowercase = false;
static constexpr size_t button_w = 240 / 5;
static constexpr size_t button_h = 28;
char txtinput[29] = { 0 }; // 28 chars max
void char_add(const char c);
void char_delete();
void set_lowercase();
void set_uppercase();
void set_keys(const char * const key_list);
void move_cursor();
void on_button(Button& button);
void update_text();
Text text_input {
{ 8, 0, 224, 16 }
@ -70,7 +68,7 @@ private:
std::array<Button, 40> buttons;
Button button_lowercase {
{ 88+64+16, 270, 32, 24 },
{ 21 * 8, 270, 32, 24 },
"UC"
};
@ -82,14 +80,10 @@ private:
'0'
};
Button button_done {
Button button_ok {
{ 88, 270, 64, 24 },
"Done"
"OK"
};
void on_button(Button& button);
void update_text();
};
} /* namespace ui */

View File

@ -28,8 +28,6 @@
#include "event_m0.hpp"
#include "message.hpp"
#include <cstdint>
namespace ui {

View File

@ -0,0 +1,409 @@
/*
* Copyright (C) 2015 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 "ui_bht_tx.hpp"
#include "portapack.hpp"
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
namespace ui {
void BHTView::focus() {
relay_states[0].focus();
}
BHTView::~BHTView() {
transmitter_model.disable();
baseband::shutdown();
}
void BHTView::generate_message() {
size_t c;
const encoder_def_t * um3750_def;
uint8_t bit[12];
uint8_t city_code;
std::string ep_symbols;
char ook_bitstream[256];
char ep_message[13] = { 0 };
if (!_mode) {
// Xy CCIR frame
// Header
ccir_message[0] = (header_code_a.value() / 10) + '0';
ccir_message[1] = (header_code_a.value() % 10) + '0';
ccir_message[2] = (header_code_b.value() / 10) + '0';
ccir_message[3] = (header_code_b.value() % 10) + '0';
// Addresses
ccir_message[4] = (city_code_xy.value() / 10) + '0';
ccir_message[5] = (city_code_xy.value() % 10) + '0';
ccir_message[6] = family_code_xy.value() + '0';
if (!checkbox_wcsubfamily.value())
ccir_message[7] = subfamily_code.value() + '0';
else
ccir_message[7] = 'A'; // Wildcard
if (!checkbox_wcid.value()) {
ccir_message[8] = (receiver_code.value() / 10) + '0';
ccir_message[9] = (receiver_code.value() % 10) + '0';
} else {
ccir_message[8] = 'A'; // Wildcard
ccir_message[9] = 'A'; // Wildcard
}
ccir_message[10] = 'B';
// Relay states
for (c = 0; c < 4; c++)
ccir_message[c + 11] = relay_states[c].selected_index() + '0';
ccir_message[15] = 'B';
// End
for (c = 16; c < 20; c++)
ccir_message[c] = '0';
ccir_message[20] = 0;
// Replace repeats with E code
for (c = 1; c < 20; c++)
if (ccir_message[c] == ccir_message[c - 1]) ccir_message[c] = 'E';
// Display as text
text_message.set(ccir_message);
ascii_to_ccir(ccir_message);
} else {
// EP frame
// Repeated 2x 26 times
// Whole frame + space = 128ms, data only = 64ms
um3750_def = &encoder_defs[8];
city_code = city_code_ep.value();
for (c = 0; c < 8; c++)
bit[c] = (city_code >> c) & 1;
bit[8] = family_code_ep.selected_index_value() >> 1;
bit[9] = family_code_ep.selected_index_value() & 1;
bit[10] = 0; // R1 first
if (relay_states[0].selected_index())
bit[11] = relay_states[0].selected_index() - 1;
else
bit[11] = 0;
for (c = 0; c < 12; c++)
ep_message[c] = bit[c] + '0';
text_message.set(ep_message);
c = 0;
for (auto ch : um3750_def->word_format) {
if (ch == 'S')
ep_symbols += um3750_def->sync;
else
ep_symbols += um3750_def->bit_format[bit[c++]];
}
c = 0;
for (auto ch : ep_symbols) {
if (ch != '0')
ook_bitstream[c >> 3] |= (1 << (7 - (c & 7)));
c++;
}
}
}
void BHTView::start_tx() {
if (speaker_enabled && !_mode)
audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
transmitter_model.set_tuning_frequency(bht_freqs[options_freq.selected_index()]);
transmitter_model.set_baseband_configuration({
.mode = 0,
.sampling_rate = 1536000U,
.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();
memcpy(shared_memory.bb_data.tones_data.message, ccir_message, 20);
for (uint8_t c = 0; c < 16; c++) {
shared_memory.bb_data.tones_data.tone_defs[c].delta = ccir_deltas[c];
shared_memory.bb_data.tones_data.tone_defs[c].duration = CCIR_TONE_LENGTH;
}
audio::set_rate(audio::Rate::Hz_24000);
baseband::set_tones_data(10000, CCIR_SILENCE, 20, false, checkbox_speaker.value());
}
// ASCII to frequency LUT index
void BHTView::ascii_to_ccir(char * ascii) {
for (size_t c = 0; c < 20; c++) {
if (ascii[c] > '9')
ascii[c] -= 0x37;
else
ascii[c] -= '0';
}
}
void BHTView::on_tx_progress(const int progress, const bool done) {
size_t c;
uint8_t sr;
if (tx_mode == SINGLE) {
if (done) {
audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
transmitter_model.disable();
progressbar.set_value(0);
if (!checkbox_cligno.value()) {
tx_mode = IDLE;
button_transmit.set_style(&style_val);
button_transmit.set_text("START");
} else {
chThdSleepMilliseconds(tempo_cligno.value() * 1000); // Dirty :(
// Invert all relay states
for (c = 0; c < 4; c++) {
sr = relay_states[c].selected_index();
if (sr > 0) relay_states[c].set_selected_index(sr ^ 3);
}
generate_message();
start_tx();
}
} else {
progressbar.set_value(progress);
}
}
}
BHTView::BHTView(NavigationView& nav) {
(void)nav;
size_t n;
baseband::run_image(portapack::spi_flash::image_tag_tones);
//baseband::run_image(portapack::spi_flash::image_tag_encoders);
add_children({ {
&options_mode,
&text_header,
&header_code_a,
&header_code_b,
&checkbox_speaker,
&bmp_speaker,
&text_city,
&city_code_xy,
&text_family,
&family_code_xy,
&text_subfamily,
&subfamily_code,
&checkbox_wcsubfamily,
&text_receiver,
&receiver_code,
&checkbox_wcid,
&text_freq,
&options_freq,
&text_relais,
&progressbar,
&text_message,
&button_transmit,
&checkbox_cligno,
&tempo_cligno,
&text_cligno
} });
options_mode.set_selected_index(0); // Xy
header_code_a.set_value(0);
header_code_b.set_value(0);
city_code_xy.set_value(18);
family_code_xy.set_value(1);
subfamily_code.set_value(1);
receiver_code.set_value(1);
options_freq.set_selected_index(0);
tempo_cligno.set_value(1);
progressbar.set_max(20);
relay_states[0].set_selected_index(1); // R1 OFF
options_mode.on_change = [this](size_t mode, OptionsField::value_t) {
_mode = mode;
if (_mode) {
// EP layout
remove_children({ {
&text_header,
&header_code_a,
&header_code_b,
&checkbox_speaker,
&bmp_speaker,
&city_code_xs,
&family_code_xy,
&text_subfamily,
&subfamily_code,
&checkbox_wcsubfamily,
&text_receiver,
&receiver_code,
&checkbox_wcid,
&relay_states[2],
&relay_states[3]
} });
add_children({ {
&city_code_ep,
&family_code_ep
} });
set_dirty();
} else {
// Xy layout
remove_children({ {
&city_code_ep,
&family_code_ep
} });
add_children({ {
&text_header,
&header_code_a,
&header_code_b,
&checkbox_speaker,
&bmp_speaker,
&city_code_xy,
&family_code_xy,
&text_subfamily,
&subfamily_code,
&checkbox_wcsubfamily,
&text_receiver,
&receiver_code,
&checkbox_wcid,
&relay_states[2],
&relay_states[3]
} });
set_dirty();
};
generate_message();
};
checkbox_speaker.on_select = [this](Checkbox&) {
speaker_enabled = checkbox_speaker.value();
};
header_code_a.on_change = [this](int32_t) {
generate_message();
};
header_code_b.on_change = [this](int32_t) {
generate_message();
};
city_code_xy.on_change = [this](int32_t) {
generate_message();
};
family_code_xy.on_change = [this](int32_t) {
generate_message();
};
subfamily_code.on_change = [this](int32_t) {
generate_message();
};
receiver_code.on_change = [this](int32_t) {
generate_message();
};
checkbox_wcsubfamily.on_select = [this](Checkbox&) {
if (checkbox_wcsubfamily.value()) {
subfamily_code.set_focusable(false);
subfamily_code.set_style(&style_grey);
text_subfamily.set_style(&style_grey);
} else {
subfamily_code.set_focusable(true);
subfamily_code.set_style(&style());
text_subfamily.set_style(&style());
}
generate_message();
};
checkbox_wcid.on_select = [this](Checkbox&) {
if (checkbox_wcid.value()) {
receiver_code.set_focusable(false);
receiver_code.set_style(&style_grey);
text_receiver.set_style(&style_grey);
} else {
receiver_code.set_focusable(true);
receiver_code.set_style(&style());
text_receiver.set_style(&style());
}
generate_message();
};
checkbox_wcsubfamily.set_value(true);
checkbox_wcid.set_value(true);
const auto relay_state_fn = [this](size_t, OptionsField::value_t) {
this->generate_message();
};
n = 0;
for (auto& relay_state : relay_states) {
relay_state.on_change = relay_state_fn;
relay_state.set_parent_rect({
static_cast<Coord>(26 + (n * 53)),
174,
24, 24
});
relay_state.set_options(relay_options);
add_child(&relay_state);
n++;
}
button_transmit.set_style(&style_val);
generate_message();
button_transmit.on_select = [this, &nav](Button&) {
if (tx_mode == IDLE) {
//auto modal_view = nav.push<ModalMessageView>("TX", "TX ?", true);
//modal_view->on_choice = [this](bool choice) {
// if (choice) {
if (speaker_enabled && _mode)
audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
tx_mode = SINGLE;
button_transmit.set_style(&style_cancel);
button_transmit.set_text("Wait");
generate_message();
start_tx();
// }
//};
}
};
}
} /* namespace ui */

View File

@ -0,0 +1,429 @@
/*
* Copyright (C) 2015 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 "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "bmp_bulb_on.hpp"
#include "bmp_bulb_off.hpp"
#include "bmp_bulb_ignore.hpp"
#include "message.hpp"
#include "volume.hpp"
#include "audio.hpp"
#include "transmitter_model.hpp"
#include "encoders.hpp"
//#include "receiver_model.hpp"
#include "portapack.hpp"
using namespace encoders;
#define CCIR_TONE_LENGTH (153600-1) // 1536000*0.1
#define CCIR_DELTA_COEF (43.691) // (65536*1024)/1536000
#define CCIR_SILENCE (614400-1) // 400ms
namespace ui {
class BHTView : public View {
public:
BHTView(NavigationView& nav);
~BHTView();
void focus() override;
std::string title() const override { return "BHT transmit"; };
private:
const uint32_t ccir_deltas[16] = {
(uint32_t)(1981 * CCIR_DELTA_COEF),
(uint32_t)(1124 * CCIR_DELTA_COEF),
(uint32_t)(1197 * CCIR_DELTA_COEF),
(uint32_t)(1275 * CCIR_DELTA_COEF),
(uint32_t)(1358 * CCIR_DELTA_COEF),
(uint32_t)(1446 * CCIR_DELTA_COEF),
(uint32_t)(1540 * CCIR_DELTA_COEF),
(uint32_t)(1640 * CCIR_DELTA_COEF),
(uint32_t)(1747 * CCIR_DELTA_COEF),
(uint32_t)(1860 * CCIR_DELTA_COEF),
(uint32_t)(2400 * CCIR_DELTA_COEF),
(uint32_t)(930 * CCIR_DELTA_COEF),
(uint32_t)(2247 * CCIR_DELTA_COEF),
(uint32_t)(991 * CCIR_DELTA_COEF),
(uint32_t)(2110 * CCIR_DELTA_COEF),
(uint32_t)(1055 * CCIR_DELTA_COEF)
};
enum tx_modes {
IDLE = 0,
SINGLE,
SEQUENCE
};
tx_modes tx_mode = IDLE;
struct bht_city {
std::string name;
uint8_t freq_index;
bool recent;
};
const bht_city bht_cities[122] = {
{ "Aizenay", 0, false },
{ "Albertville", 3, false },
{ "Ales", 3, false },
{ "Artannes/Indre", 5, false },
{ "Avignon", 3, true },
{ "Azay-le-Rideau", 5, false },
{ "Baux Ste. Croix", 0, false },
{ "Beaugency", 4, false },
{ "Beaune", 4, false },
{ "Betton", 2, false },
{ "Bihorel", 0, true },
{ "Blanquefort", 4, true },
{ "Bobigny", 5, false },
{ "Bouffere", 4, true },
{ "Boulogne/Mer", 0, true },
{ "Bourg-en-Bresse", 3, false },
{ "Bourges", 0, false },
{ "Bouscat", 0, false },
{ "Carquefou", 5, false },
{ "St. Cast", 0, false },
{ "Caudebec/Caux", 3, true },
{ "Cercy-la-Tour", 5, false },
{ "Chamalieres", 5, false },
{ "St. Chamond", 5, false },
{ "Chapelle/Fgrtz", 2, false },
{ "Charite/Loire", 3, false },
{ "Charleville-Mzr", 1, false },
{ "Chilly Mazarin", 5, false },
{ "Clermont Frrd.", 5, false },
{ "Cluses", 2, false },
{ "Compiegne", 4, false },
{ "Coulanges/Nevers", 5, false },
{ "Cour Cheverny", 5, false },
{ "Cournon Auvergne", 5, false },
{ "Crolles", 5, true },
{ "Cublize", 4, true },
{ "Donges", 5, false },
{ "Emalleville", 0, false },
{ "Etrepagny", 0, false },
{ "Fecamp", 0, false },
{ "Ferriere", 0, false },
{ "Ferte Imbault", 5, false },
{ "Fontaine", 5, true },
{ "Forbach", 3, false },
{ "Fourchambault", 5, false },
{ "Fresnay/Sarthe", 3, false },
{ "St Fulgent", 5, true },
{ "Gaillac", 3, true },
{ "St. Georges/Grs", 0, false },
{ "St. Gervais/Frt", 5, false },
{ "Givors", 5, false },
{ "Guichen", 2, false },
{ "Guildo", 0, false },
{ "Guipry", 2, false },
{ "St Hilaire/Riez", 0, false },
{ "Hossegor/Capbrtn", 0, true },
{ "Houlbec-Cocherel", 0, false },
{ "Huisseau/Cosson", 5, false },
{ "Huningue", 5, false },
{ "Iffendic", 2, false },
{ "La Croix St. Ouen", 4, false },
{ "Langrune/Mer", 0, false },
{ "Le Neubourg", 2, true },
{ "St Leger/Vignes", 5, false },
{ "Levallois-Perret", 5, false },
{ "Lille", 5, true },
{ "Limoges", 5, false },
{ "Longueil-Anel", 4, false },
{ "Lormont", 5, true },
{ "Mantes-la-Jolie", 5, false },
{ "Martigues", 0, true },
{ "Marzy", 5, false },
{ "Ste. Memmie", 3, false },
{ "Menton", 0, true },
{ "Metz", 3, false },
{ "Mezidon Canon", 1, false },
{ "Millau", 5, false },
{ "Miniac-Morvan", 2, false },
{ "Mt. Pres Chambord", 5, false },
{ "Montesson", 5, false },
{ "Monts", 5, false },
{ "Noisy-le-Grand", 4, true },
{ "St Ouen", 5, false },
{ "Ozoir/Ferriere", 5, false },
{ "Pace", 2, false },
{ "Pelussin", 5, false },
{ "Petite Foret", 1, false },
{ "Plestin/Greves", 0, false },
{ "Pleumeur Bodou", 5, true },
{ "Pont Audemer", 0, false },
{ "Pontcharra", 5, true },
{ "Pontchateau", 5, false },
{ "Pressagny L'Org.", 0, false },
{ "Remiremont", 4, true },
{ "Ribeauville", 5, false },
{ "La Roche sur Yon", 0, false },
{ "Romorantin-Lant.", 5, false },
{ "Rueil Malmaison", 5, false },
{ "Sault-les-Rethel", 3, false },
{ "Selles-St-Denis", 5, false },
{ "Selles/Cher", 5, false },
{ "Sens", 4, false },
{ "Sezanne", 3, false },
{ "Sommesous", 3, false },
{ "Ste. Suzanne", 2, true },
{ "Talence", 3, true },
{ "Thionville", 3, false },
{ "Thonon-les-Bains", 2, false },
{ "Tours en Sologne", 5, true },
{ "Trelaze", 5, true },
{ "Trouville/Mer", 0, false },
{ "Tulle", 2, false },
{ "Ussel", 2, false },
{ "Valberg", 5, true },
{ "Valence", 5, false },
{ "Velizy", 5, false },
{ "Vesoul", 5, false },
{ "Ville S. la Ferte", 0, false },
{ "Villefrance/Saone", 5, false },
{ "Villers Cotterets", 3, false },
{ "Vitre", 2, false },
{ "Vitry-le-Francois", 4, true }
};
const rf::Frequency bht_freqs[7] = { 31325000, 31387500, 31437500, 31475000, 31687500, 31975000, 88000000 };
char ccir_message[21];
bool speaker_enabled = false;
size_t _mode = 0;
void ascii_to_ccir(char * ascii);
void start_tx();
void generate_message();
void on_tx_progress(const int progress, const bool done);
const Style style_val {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
const Style style_cancel {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
const Style style_grey {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::grey(),
};
OptionsField options_mode {
{ 10 * 8, 4 },
10,
{
{ "Mode Xy.", 0 },
{ "Mode EP.", 1 }
}
};
Checkbox checkbox_speaker {
{ 22 * 8, 4 },
0,
""
};
Image bmp_speaker {
{ 204, 8, 16, 16 },
&bitmap_speaker,
ui::Color::white(),
ui::Color::black()
};
Text text_header {
{ 8 * 8, 3 * 8, 7 * 8, 16 },
"Header:"
};
NumberField header_code_a {
{ 16 * 8, 3 * 8 },
2,
{ 0, 99 },
1,
'0'
};
NumberField header_code_b {
{ 18 * 8, 3 * 8 },
2,
{ 0, 99 },
1,
'0'
};
Text text_city {
{ 4 * 8, 5 * 8, 11 * 8, 16 },
"Code ville:"
};
NumberField city_code_xy {
{ 16 * 8, 5 * 8 },
2,
{ 0, 99 },
1,
' '
};
NumberField city_code_ep {
{ 16 * 8, 5 * 8 },
3,
{ 0, 255 },
1,
' '
};
Text text_family {
{ 7 * 8, 7 * 8, 8 * 8, 16 },
"Famille:"
};
NumberField family_code_xy {
{ 16 * 8, 7 * 8 },
1,
{ 0, 9 },
1,
' '
};
OptionsField family_code_ep {
{ 16 * 8, 7 * 8 },
2,
{
{ "A ", 2 }, // See receiver PCB
{ "B ", 1 },
{ "C ", 0 },
{ "TP", 3 }
}
};
Text text_subfamily {
{ 2 * 8, 9 * 8 + 2, 13 * 8, 16 },
"Sous-famille:"
};
NumberField subfamily_code {
{ 16 * 8, 9 * 8 + 2 },
1,
{ 0, 9 },
1,
' '
};
Checkbox checkbox_wcsubfamily {
{ 20 * 8, 8 * 8 + 6 },
6,
"Toutes"
};
Text text_receiver {
{ 2 * 8, 13 * 8, 13 * 8, 16 },
"ID recepteur:"
};
NumberField receiver_code {
{ 16 * 8, 13 * 8 },
2,
{ 0, 99 },
1,
'0'
};
Checkbox checkbox_wcid {
{ 20 * 8, 12 * 8 + 4 },
4,
"Tous"
};
Text text_freq {
{ 6 * 8, 8 * 16, 10 * 8, 16 },
"Frequence:"
};
OptionsField options_freq {
{ 17 * 8, 8 * 16},
7,
{
{ "31.3250", 0 },
{ "31.3875", 1 },
{ "31.4375", 2 },
{ "31.4750", 3 },
{ "31.6875", 4 },
{ "31.9750", 5 },
{ "TEST 88", 6 }
}
};
Text text_relais {
{ 8, 19 * 8, 13 * 8, 16 },
"Etats relais:"
};
std::array<ImageOptionsField, 4> relay_states;
ImageOptionsField::options_t relay_options = {
{ &bulb_ignore_bmp[0], 0 },
{ &bulb_off_bmp[0], 1 },
{ &bulb_on_bmp[0], 2 }
};
ProgressBar progressbar {
{ 5 * 8, 27 * 8, 20 * 8, 16 },
};
Text text_message {
{ 5 * 8, 29 * 8, 20 * 8, 16 },
""
};
Button button_transmit {
{ 2 * 8, 16 * 16, 12 * 8, 32 },
"START"
};
Checkbox checkbox_cligno {
{ 16 * 8, 16 * 16 + 4},
3,
"J/N"
};
NumberField tempo_cligno {
{ 24 * 8, 16 * 16 + 8},
2,
{ 1, 99 },
1,
' '
};
Text text_cligno {
{ 26 * 8, 16 * 16 + 8, 2 * 8, 16 },
"s."
};
MessageHandlerRegistration message_handler_tx_done {
Message::ID::TXDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
this->on_tx_progress(message.progress, message.done);
}
};
};
} /* namespace ui */

View File

@ -23,13 +23,9 @@
#include "ui_closecall.hpp"
#include "msgpack.hpp"
#include "ch.h"
#include "time.hpp"
#include "event_m0.hpp"
#include "hackrf_gpio.hpp"
#include "portapack.hpp"
#include "radio.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"

View File

@ -25,8 +25,6 @@
#include "spectrum_color_lut.hpp"
#include "ui_receiver.hpp"
#include "ui_spectrum.hpp"
#include "ui_record_view.hpp"
#include "ui_font_fixed_8x16.hpp"
namespace ui {

View File

@ -208,36 +208,6 @@ private:
};
};
class DebugSDView : public View {
public:
DebugSDView(NavigationView& nav);
void focus() override;
void paint(Painter& painter) override;
private:
Text text_title {
{ 32, 16, 128, 16 },
"SD card debug"
};
Text text_modules {
{ 8, 32, 28 * 8, 16 },
"TESTTESTTESTTESTTESTTESTTEST"
};
Button button_makefile {
{ 72, 192, 96, 24 },
"Play file"
};
Button button_done {
{ 72, 240, 96, 24 },
"Done"
};
};
class DebugLCRView : public View {
public:
DebugLCRView(NavigationView& nav, std::string lcrstring, uint8_t checksum);

View File

@ -210,7 +210,7 @@ void EncodersView::start_tx(const bool scan) {
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
memcpy(shared_memory.tx_data, ook_bitstream, 256);
memcpy(shared_memory.bb_data.data, ook_bitstream, 256);
baseband::set_ook_data(
ook_bitstream_length,
@ -233,7 +233,7 @@ void EncodersView::on_type_change(size_t index) {
encoder_def = &encoder_defs[enc_type];
numberfield_clk.set_value(encoder_def->default_frequency / 1000);
numberfield_clk.set_value(encoder_def->default_speed / 1000);
// SymField setup
word_length = encoder_def->word_length;

View File

@ -24,12 +24,13 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "encoders.hpp"
#include "message.hpp"
#include "transmitter_model.hpp"
namespace ui {
using namespace encoders;
#define ENC_TYPES_COUNT 14
namespace ui {
class EncodersView : public View {
public:
@ -43,191 +44,6 @@ public:
std::string title() const override { return "Encoders TX"; };
private:
struct encoder_def_t {
std::string name; // Encoder chip ref/name
std::string address_symbols; // "01", "01F"...
std::string data_symbols; // Same
uint16_t clk_per_symbol; // Oscillator periods per symbol
uint16_t clk_per_fragment; // Oscillator periods per symbol fragment (state)
std::vector<std::string> bit_format; // List of fragments for each symbol in previous *_symbols list order
uint8_t word_length; // Total # of symbols (not counting sync)
std::string word_format; // A for Address, D for Data, S for sync
std::string sync; // Like bit_format
uint32_t default_frequency; // Default encoder clk frequency (often set by shitty resistor)
uint8_t repeat_min; // Minimum repeat count
uint16_t pause_symbols; // Length of pause between repeats in symbols
};
const encoder_def_t encoder_defs[ENC_TYPES_COUNT] = {
// PT2260-R2
{
"2260-R2",
"01F", "01",
1024, 128,
{ "10001000", "11101110", "10001110" },
12, "AAAAAAAAAADDS",
"10000000000000000000000000000000",
150000, 2,
0
},
// PT2260-R4
{
"2260-R4",
"01F", "01",
1024, 128,
{ "10001000", "11101110", "10001110" },
12, "AAAAAAAADDDDS",
"10000000000000000000000000000000",
150000, 2,
0
},
// PT2262
{
"2262 ",
"01F", "01F",
32, 4,
{ "10001000", "11101110", "10001110" },
12, "AAAAAAAAAAAAS",
"10000000000000000000000000000000",
20000, 4,
0
},
// 16-bit ?
{
"16-bit ",
"01", "01",
32, 8,
{ "1110", "1000" }, // Opposite ?
16, "AAAAAAAAAAAAAAAAS",
"100000000000000000000",
25000, 50,
0 // ?
},
// RT1527
{
"1527 ",
"01", "01",
128, 32,
{ "1000", "1110" },
24, "SAAAAAAAAAAAAAAAAAAAADDDD",
"10000000000000000000000000000000",
100000, 4,
10 // ?
},
// HK526E
{
"526 ",
"01", "01",
24, 8,
{ "110", "100" },
12, "AAAAAAAAAAAA",
"",
20000, 4,
10 // ?
},
// HT12E
{
"12E ",
"01", "01",
3, 1,
{ "011", "001" },
12, "SAAAAAAAADDDD",
"0000000000000000000000000000000000001",
3000, 4,
10 // ?
},
// VD5026 13 bits ?
{
"5026 ",
"0123", "0123",
128, 8,
{ "1000000010000000", "1111111011111110", "1111111010000000", "1000000011111110" },
12, "SAAAAAAAAAAAA",
"000000000000000000000000000000000000000000000001", // ?
100000, 4,
10 // ?
},
// UM3750
{
"UM3750 ",
"01", "01",
96, 32,
{ "011", "001" },
12, "SAAAAAAAAAAAA",
"1",
100000, 4,
0 // ?
},
// UM3758
{
"UM3758 ",
"01F", "01",
96, 16,
{ "011011", "001001", "011001" },
18, "SAAAAAAAAAADDDDDDDD",
"1",
160000, 4,
10 // ?
},
// BA5104
{
"BA5104 ",
"01", "01",
3072, 768,
{ "1000", "1110" },
9, "SDDAAAAAAA",
"",
455000, 4,
10 // ?
},
// MC145026
{
"145026 ",
"01F", "01",
16, 1,
{ "0111111101111111", "0100000001000000", "0111111101000000" },
9, "SAAAAADDDD",
"000000000000000000",
455000, 2,
2
},
// HT6*** TODO: Add individual variations
{
"HT6*** ",
"01F", "01",
198, 33,
{ "011011", "001001", "001011" },
18, "SAAAAAAAAAAAADDDDDD",
"0000000000000000000000000000000000001011001011001",
80000, 3,
10 // ?
},
// TC9148
{
"TC9148 ",
"01", "01",
48, 12,
{ "1000", "1110", },
12, "AAAAAAAAAAAA",
"",
455000, 3,
10 // ?
}
};
enum tx_modes {
IDLE = 0,
SINGLE,
@ -373,7 +189,7 @@ private:
Message::ID::TXDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
this->on_txdone(message.n);
this->on_txdone(message.progress);
}
};
};

View File

@ -1,235 +0,0 @@
/*
* Copyright (C) 2015 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 "ui_epar.hpp"
#include "ch.h"
#include "hackrf_hal.hpp"
#include "event_m0.hpp"
#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;
namespace ui {
void EPARView::focus() {
city_code.focus();
}
EPARView::~EPARView() {
transmitter_model.disable();
}
void EPARView::update_message() {
uint8_t c;
// Start bit
epar_bits[0] = 1;
// To binary
for (c = 0; c < 8; c++)
epar_bits[c + 1] = ((city_code.value() >> c) & 1);
epar_bits[9] = options_group.selected_index_value() & 1;
epar_bits[10] = (options_group.selected_index_value() >> 1) & 1;
if (checkbox_ra.value())
epar_bits[11] = 1; // Bit 11 is relay 1 state
else
epar_bits[11] = 0;
if (checkbox_rb.value())
epar_bits[12] = 1; // Bit 12 is relay 2 state
else
epar_bits[12] = 0;
// DEBUG
char debug_binary[14];
for (c = 0; c < 13; c++)
debug_binary[c] = 0x30 + epar_bits[c];
debug_binary[13] = 0;
text_debug.set(debug_binary);
}
void EPARView::journuit() {
chThdSleepMilliseconds(1000);
// Invert relay states
checkbox_ra.set_value(~checkbox_ra.value());
checkbox_rb.set_value(~checkbox_rb.value());
update_message();
shared_memory.transmit_done = false;
memcpy(shared_memory.epardata, epar_bits, 13);
transmitter_model.enable();
}
void EPARView::on_tuning_frequency_changed(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
EPARView::EPARView(
NavigationView& nav
)
{
static constexpr Style style_val {
.font = font::fixed_8x16,
.background = Color::green(),
.foreground = Color::black(),
};
static constexpr Style style_cancel {
.font = font::fixed_8x16,
.background = Color::red(),
.foreground = Color::black(),
};
transmitter_model.set_baseband_configuration({
.mode = 4,
.sampling_rate = 1536000,
.decimation_factor = 1,
});
add_children({ {
&text_city,
&city_code,
&text_group,
&options_group,
&checkbox_ra,
&checkbox_rb,
&excur,
&text_freq,
//&options_freq,
&field_frequency,
&progressbar,
&text_debug,
&button_transmit,
&checkbox_cligno,
&button_exit
} });
city_code.set_value(220);
options_group.set_selected_index(3); // TP
//options_freq.set_selected_index(6); // 5 ! DEBUG
checkbox_ra.set_value(true);
checkbox_rb.set_value(true);
excur.set_value(500);
shared_memory.excursion = 500;
excur.on_change = [this](int32_t v) {
(void)v;
shared_memory.excursion = excur.value();
};
field_frequency.set_value(31387500); // 31.3805 receiver_model.tuning_frequency()
field_frequency.set_step(500);
field_frequency.on_change = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
this->field_frequency.set_value(f);
};
};
city_code.on_change = [this](int32_t v) {
(void)v;
EPARView::update_message();
};
options_group.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
(void)v;
EPARView::update_message();
};
checkbox_ra.on_select = [this](Checkbox&) {
EPARView::update_message();
};
checkbox_rb.on_select = [this](Checkbox&) {
EPARView::update_message();
};
button_transmit.set_style(&style_val);
EPARView::update_message();
button_transmit.on_select = [this](Button&) {
if (txing == false) {
update_message();
EventDispatcher::message_map().unregister_handler(Message::ID::TXDone);
EventDispatcher::message_map().register_handler(Message::ID::TXDone,
[this](Message* const p) {
const auto message = static_cast<const TXDoneMessage*>(p);
if (message->n == 100) {
transmitter_model.disable();
progressbar.set_value(0);
if (checkbox_cligno.value() == false) {
txing = false;
button_transmit.set_style(&style_val);
button_transmit.set_text("START");
} else {
journuit();
}
} else {
progressbar.set_value(message->n * 2); // 100/52
}
}
);
shared_memory.transmit_done = false;
memcpy(shared_memory.epardata, epar_bits, 13);
transmitter_model.set_tuning_frequency(field_frequency.value());
//transmitter_model.set_tuning_frequency(epar_freqs[options_freq.selected_index()]);
txing = true;
button_transmit.set_style(&style_cancel);
button_transmit.set_text("Wait");
transmitter_model.enable();
}
};
button_exit.on_select = [&nav](Button&){
nav.pop();
};
}
} /* namespace ui */

View File

@ -1,155 +0,0 @@
/*
* Copyright (C) 2015 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 "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 "ui_receiver.hpp"
#include "clock_manager.hpp"
#include "message.hpp"
#include "rf_path.hpp"
#include "max2837.hpp"
#include "volume.hpp"
#include "transmitter_model.hpp"
#include "receiver_model.hpp"
namespace ui {
class EPARView : public View {
public:
EPARView(NavigationView& nav);
~EPARView();
std::string title() const override { return "EPAR transmit"; };
void journuit();
void talk();
void update_message();
void focus() override;
private:
bool txing = false;
const rf::Frequency epar_freqs[7] = { 31325000, 31387500, 31437500, 31475000, 31687500, 31975000, 88000000 };
char epar_bits[13];
void on_tuning_frequency_changed(rf::Frequency f);
/* |012345678901234567890123456789|
* | Code ville: 000 |
* | Groupe: 00 |
* */
Text text_city {
{ 6 * 8, 1 * 16, 11 * 8, 16 },
"Code ville:"
};
NumberField city_code {
{ 18 * 8, 1 * 16 },
3,
{ 0, 255 },
0,
' '
};
Text text_group {
{ 10 * 8, 2 * 16, 7 * 8, 16 },
"Groupe:"
};
OptionsField options_group {
{ 18 * 8, 2 * 16 },
4,
{
{ "A ", 2 }, // See receiver PCB
{ "B ", 1 },
{ "C ", 0 },
{ "TP", 3 }
}
};
Text text_freq {
{ 5 * 8, 4 * 16, 10 * 8, 16 },
"Frequence:"
};
FrequencyField field_frequency {
{ 16 * 8, 4 * 16 },
};
/*OptionsField options_freq {
{ 16 * 8, 4 * 16},
7,
{
{ "31.3250", 0 },
{ "31.3875", 1 },
{ "31.4375", 2 },
{ "31.4750", 3 },
{ "31.6875", 4 },
{ "31.9750", 5 },
{ "TEST 88", 6 }
}
};*/
Checkbox checkbox_ra {
{ 7 * 8, 6 * 16 },
8,
"Relais 1"
};
Checkbox checkbox_rb {
{ 7 * 8, 8 * 16 },
8,
"Relais 2"
};
NumberField excur {
{ 12 * 8, 10 * 16 },
4,
{ 0, 5000 },
20,
' '
};
ProgressBar progressbar {
{ 2 * 8, 12 * 16, 26 * 8, 20 },
};
Text text_debug {
{ 5 * 8, 14 * 16, 13 * 8, 16 },
"-------------"
};
Button button_transmit {
{ 2 * 8, 16 * 16, 64, 32 },
"START"
};
Checkbox checkbox_cligno {
{ 96, 16 * 16 + 4},
3,
"J/N"
};
Button button_exit {
{ 21 * 8, 16 * 16, 64, 32 },
"Exit"
};
};
} /* namespace ui */

View File

@ -22,8 +22,6 @@
#include "ui_freqman.hpp"
#include "ch.h"
#include "ff.h"
#include "portapack.hpp"
#include "event_m0.hpp"
#include "portapack_shared_memory.hpp"
@ -35,13 +33,36 @@ using namespace portapack;
namespace ui {
void FrequencySaveView::focus() {
button_exit.focus();
button_save_timestamp.focus();
}
FrequencySaveView::FrequencySaveView(
NavigationView& nav,
const rf::Frequency value
) {
add_children({ {
&big_display,
&text_save,
&button_save_name,
&button_save_timestamp,
&button_cancel
} });
big_display.set(value);
button_cancel.on_select = [this, &nav](Button&) {
nav.pop();
};
}
void FrequencyLoadView::focus() {
button_exit.focus();
}
FrequencyLoadView::FrequencyLoadView(
NavigationView& nav,
const rf::Frequency value
) {
add_children({ {
&button_exit

View File

@ -37,6 +37,40 @@ public:
std::string title() const override { return "Save frequency"; };
private:
BigFrequency big_display {
{ 4, 2 * 16, 28 * 8, 32 },
0
};
Text text_save {
{ 72, 124, 10 * 8, 16 },
"Save with:",
};
Button button_save_name {
{ 72, 144, 96, 32 },
"Name"
};
Button button_save_timestamp {
{ 72, 184, 96, 32 },
"Timestamp"
};
Button button_cancel {
{ 72, 264, 96, 32 },
"Cancel"
};
};
class FrequencyLoadView : public View {
public:
FrequencyLoadView(NavigationView& nav, const rf::Frequency value);
//~FrequencySaveView();
void focus() override;
std::string title() const override { return "Load frequency"; };
private:
Button button_exit {
{ 72, 264, 96, 32 },

View File

@ -22,11 +22,7 @@
#include "ui_handwrite.hpp"
#include "ch.h"
#include "ff.h"
#include "portapack.hpp"
#include "event_m0.hpp"
#include "hackrf_hal.hpp"
#include "portapack_shared_memory.hpp"
@ -43,17 +39,17 @@ void HandWriteView::paint(Painter& painter) {
HandWriteView::HandWriteView(
NavigationView& nav,
char txt[],
uint8_t max_len
) {
size_t max_length
) : _max_length(max_length)
{
const char special_chars[5] = {'\'', '.', '?', '!', '='};
_max_len = max_len;
size_t n;
// Handwriting alphabet definition here
handwriting = &handwriting_unistroke;
txtidx = strlen(txt);
memcpy(txtinput, txt, max_len + 1);
memcpy(txtinput, txt, _max_length + 1);
n = txtidx;
while (n && (txtinput[n - 1] == ' ')) {
txtinput[--n] = 0;
@ -71,7 +67,7 @@ HandWriteView::HandWriteView(
};
n = 0;
for(auto& button : num_buttons) {
for (auto& button : num_buttons) {
add_child(&button);
button.on_select = button_fn;
button.set_parent_rect({
@ -80,15 +76,15 @@ HandWriteView::HandWriteView(
24, 28
});
const std::string label {
(char)(n + 0x30)
(char)(n + '0')
};
button.set_text(label);
button.id = n + 0x30;
button.id = n + '0';
n++;
}
n = 0;
for(auto& button : special_buttons) {
for (auto& button : special_buttons) {
add_child(&button);
button.on_select = button_fn;
button.set_parent_rect({
@ -114,9 +110,9 @@ HandWriteView::HandWriteView(
}
};
button_ok.on_select = [this, &nav, txt, max_len](Button&) {
memcpy(txt, txtinput, max_len + 1);
on_changed(this->value());
button_ok.on_select = [this, &nav, txt, max_length](Button&) {
memcpy(txt, txtinput, max_length + 1);
if (on_changed) on_changed(this->value());
nav.pop();
};
@ -134,14 +130,13 @@ bool HandWriteView::on_touch(const TouchEvent event) {
guess_letter();
}
if (event.type == ui::TouchEvent::Type::Move) {
if (tracing) {
if (tracing)
current_pos = event.point;
}
}
return true;
}
void HandWriteView::clear_zone(Color color, bool flash) {
void HandWriteView::clear_zone(const Color color, const bool flash) {
display.fill_rectangle(
{{0, 32}, {240, 216}},
color
@ -149,6 +144,7 @@ void HandWriteView::clear_zone(Color color, bool flash) {
if (flash) {
flash_timer = 4;
} else {
// Draw grid
_painter->draw_rectangle(
{{0, 32}, {80, 216}},
Color::grey()
@ -169,7 +165,7 @@ void HandWriteView::clear_zone(Color color, bool flash) {
}
void HandWriteView::guess_letter() {
uint8_t symbol, match, count, stroke_idx, stroke_data;
uint32_t symbol, match, count, stroke_idx, stroke_data;
Condition cond;
Direction dir;
bool matched;
@ -212,7 +208,7 @@ void HandWriteView::guess_letter() {
} else if ((dir & 0x0F) == 0x0F) {
if ((dir & 0xF0) != (stroke_data & 0xF0)) break;
} else {
if (dir != stroke_data) break;
if (dir != (int32_t)stroke_data) break;
}
}
}
@ -231,7 +227,8 @@ void HandWriteView::guess_letter() {
clear_zone(Color::green(), true); // Green flash
} else {
if (txtidx) {
txtinput[--txtidx] = 0; // Erase
txtidx--;
txtinput[txtidx] = 0; // Erase
clear_zone(Color::yellow(), true); // Yellow flash
} else {
clear_zone(Color::red(), true); // Red flash
@ -386,6 +383,7 @@ void HandWriteView::on_show() {
}
char * HandWriteView::value() {
txtinput[txtidx] = 0;
return txtinput;
}
@ -395,10 +393,10 @@ void HandWriteView::on_button(Button& button) {
}
void HandWriteView::char_add(const char c) {
if (txtidx < _max_len) {
txtinput[txtidx] = c;
txtidx++;
}
if (txtidx >= _max_length) return;
txtinput[txtidx] = c;
txtidx++;
}
void HandWriteView::update_text() {

View File

@ -26,10 +26,8 @@
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "unistroke.hpp"
#include "message.hpp"
namespace ui {
@ -37,20 +35,20 @@ class HandWriteView : public View {
public:
std::function<void(char *)> on_changed;
HandWriteView(NavigationView& nav, char txt[], uint8_t max_len);
HandWriteView(NavigationView& nav, char txt[], size_t max_length);
void paint(Painter& painter) override;
void on_show() override;
bool on_touch(const TouchEvent event) override;
char * value();
void char_add(const char c);
std::string title() const override { return "Text entry"; };
private:
const HandWriting * handwriting;
Painter * _painter;
uint8_t _max_len;
size_t _max_length;
uint8_t dir_cnt = 0;
uint8_t dir_prev;
uint8_t flash_timer = 0;
@ -61,12 +59,16 @@ private:
uint8_t sample_skip, move_wait;
uint8_t stroke_list[8];
Point start_pos, current_pos, last_pos;
bool _lowercase = true;
char txtinput[25] = {0};
bool _lowercase = false;
char txtinput[29] = { 0 }; // 28 chars max
void sample_pen();
void add_stroke(uint8_t dir);
void guess_letter();
void clear_zone(Color color, bool flash);
void clear_zone(const Color color, const bool flash);
void char_add(const char c);
void on_button(Button& button);
void update_text();
Text text_input {
{ 8, 0, 224, 16 }
@ -84,10 +86,6 @@ private:
{ 190, 270, 40, 28 },
"OK"
};
void on_button(Button& button);
void update_text();
MessageHandlerRegistration message_handler_sample {
Message::ID::DisplayFrameSync,

View File

@ -118,7 +118,7 @@ JammerView::JammerView(NavigationView& nav) {
.foreground = Color::grey(),
};
JammerRange * jammer_ranges = (JammerRange*)shared_memory.tx_data;
JammerRange * jammer_ranges = (JammerRange*)shared_memory.bb_data.data;
add_children({ {
&text_type,

View File

@ -259,7 +259,7 @@ void LCRView::start_tx(const bool scan) {
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
memcpy(shared_memory.tx_data, lcr_message_data, 300);
memcpy(shared_memory.bb_data.data, lcr_message_data, 300);
baseband::set_afsk_data(
(153600 * 5) / portapack::persistent_memory::afsk_bitrate(),

View File

@ -179,7 +179,7 @@ private:
Message::ID::TXDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
this->on_txdone(message.n);
this->on_txdone(message.progress);
}
};
};

View File

@ -49,19 +49,20 @@ void MenuItemView::paint(Painter& painter) {
const auto paint_style = (highlighted() && parent()->has_focus()) ? style().invert() : style();
const auto font_height = paint_style.font.line_height();
ui::Color final_item_color = (highlighted() && parent()->has_focus()) ? paint_style.foreground : item.color;
ui::Color final_bg_color = (highlighted() && parent()->has_focus()) ? item.color : paint_style.background;
if (final_item_color.v == final_bg_color.v) final_item_color = paint_style.foreground;
painter.fill_rectangle(
r,
paint_style.background
final_bg_color
);
ui::Color final_item_color = (highlighted() && parent()->has_focus()) ? ui::Color::black() : item.color;
if (final_item_color.v == paint_style.background.v) final_item_color = paint_style.foreground;
Style text_style {
.font = paint_style.font,
.background = paint_style.background,
.background = final_bg_color,
.foreground = final_item_color
};
@ -84,6 +85,7 @@ MenuView::MenuView() {
arrow_more.set_parent_rect( { 216, 320 - 16 - 24, 16, 16 } );
arrow_more.set_focusable(false);
arrow_more.set_foreground(Color::black());
}
MenuView::~MenuView() {
@ -115,7 +117,7 @@ void MenuView::update_items() {
size_t i = 0;
Coord y_pos;
if (MENU_MAX + offset_ < (children_.size() - 1))
if ((children_.size() - 1) > MENU_MAX + offset_)
more_ = true;
else
more_ = false;
@ -148,9 +150,8 @@ size_t MenuView::highlighted() const {
}
bool MenuView::set_highlighted(const size_t new_value) {
if( new_value >= (children_.size() - 1) ) { // Skip arrow widget
if( new_value >= (children_.size() - 1) ) // Skip arrow widget
return false;
}
if ((new_value > offset_) && ((new_value - offset_ + 1) >= MENU_MAX)) {
// Shift MenuView up

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*

View File

@ -0,0 +1,143 @@
/*
* Copyright (C) 2015 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.
*/
/*
Keying speed: 60 or 75 PARIS
Continuous (Fox-oring)
12s transmit, 48s space (Sprint 1/5th)
60s transmit, 240s space (Classic 1/5 min)
60s transmit, 360s space (Classic 1/7 min)
*/
#include "ui_morse.hpp"
#include "portapack.hpp"
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
// TODO: TX power setting
// TODO: Live keying mode: Dit on left key, dah on right ?
namespace ui {
void MorseView::focus() {
button_transmit.focus();
}
MorseView::~MorseView() {
transmitter_model.disable();
baseband::shutdown();
}
void MorseView::paint(Painter& painter) {
(void)painter;
}
void MorseView::generate_message(char * text) {
char ch;
size_t i, c;
uint8_t code;
uint8_t morse_message[256];
ToneDef * tone_defs = shared_memory.bb_data.tones_data.tone_defs;
// TODO: OOB check on morse_message[]
i = 0;
while ((ch = (*text++))) {
if ((ch >= 'a') && (ch <= 'z')) // Make uppercase
ch -= 32;
if ((ch >= 'A') && (ch <= 'Z')) {
code = morse_ITU[ch - 'A' + 10];
for (c = 0; c < (code & 7); c++) {
morse_message[i++] = (code << c) >> 7; // Dot/dash
morse_message[i++] = 2; // Silence
}
morse_message[i - 1] = 3; // Letter silence
} else if (ch == ' ') {
morse_message[i++] = 4; // Word silence
}
}
memcpy(shared_memory.bb_data.tones_data.message, morse_message, i);
(*tone_defs).delta = MORSE_TONE_DELTA; // Dot
(*tone_defs++).duration = MORSE_DOT;
(*tone_defs).delta = MORSE_TONE_DELTA; // Dash
(*tone_defs++).duration = MORSE_DASH;
(*tone_defs).delta = 0; // 1 unit silence
(*tone_defs++).duration = MORSE_SPACE;
(*tone_defs).delta = 0; // 3 unit silence
(*tone_defs++).duration = MORSE_LETTER_SPACE;
(*tone_defs).delta = 0; // 7 unit silence
(*tone_defs++).duration = MORSE_WORD_SPACE;
audio::set_rate(audio::Rate::Hz_24000);
baseband::set_tones_data(5000, 0, i, false, false);
}
MorseView::MorseView(
NavigationView& nav
)
{
add_children({ {
&options_foxhunt,
&button_transmit,
&button_exit
} });
button_transmit.on_select = [this](Button&){
/*uint16_t c;
ui::Context context;
make_frame();
shared_memory.afsk_samples_per_bit = 228000/persistent_memory::afsk_bitrate();
shared_memory.afsk_phase_inc_mark = persistent_memory::afsk_mark_freq()*(65536*1024)/2280;
shared_memory.afsk_phase_inc_space = persistent_memory::afsk_space_freq()*(65536*1024)/2280;
for (c = 0; c < 256; c++) {
shared_memory.lcrdata[c] = this->lcrframe[c];
}
shared_memory.afsk_transmit_done = false;
shared_memory.afsk_repeat = 5; // DEFAULT
text_status.set("Send...");*/
//transmitter_model.enable();
};
button_exit.on_select = [&nav](Button&){
nav.pop();
};
}
} /* namespace ui */

View File

@ -0,0 +1,198 @@
/*
* Copyright (C) 2015 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 "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "message.hpp"
#include "volume.hpp"
#include "audio.hpp"
#include "transmitter_model.hpp"
//#include "receiver_model.hpp"
#include "portapack.hpp"
#define MORSE_TONE_DELTA ((1536000 / 800) - 1) // 1536000/800
#define MORSE_UNIT (76800 - 1) // 1536000*0.05 (50ms)
#define MORSE_DOT 1 * MORSE_UNIT
#define MORSE_DASH 3 * MORSE_UNIT
#define MORSE_SPACE 1 * MORSE_UNIT
#define MORSE_LETTER_SPACE 3 * MORSE_UNIT
#define MORSE_WORD_SPACE 7 * MORSE_UNIT
namespace ui {
class MorseView : public View {
public:
MorseView(NavigationView& nav);
~MorseView();
void focus() override;
void paint(Painter& painter) override;
private:
//rf::Frequency f;
void generate_message(char * text);
const char foxhunt_codes[11][3] = {
{ 'M', 'O', 'E' },
{ 'M', 'O', 'I' },
{ 'M', 'O', 'S' },
{ 'M', 'O', 'H' },
{ 'M', 'O', '5' },
{ 'M', 'O', 'N' },
{ 'M', 'O', 'D' },
{ 'M', 'O', 'B' },
{ 'M', 'O', '6' },
{ 'M', 'O', 0 },
{ 'S', 0, 0 }
};
// 0=dot 1=dash
const uint8_t morse_ITU[36] = {
// Code Size
0b11111101, // 0: 11111 101
0b01111101, // 1: 01111 101
0b00111101, // 2: 00111 101
0b00011101, // 3: 00011 101
0b00001101, // 4: 00001 101
0b00000101, // 5: 00000 101
0b10000101, // 6: 10000 101
0b11000101, // 7: 11000 101
0b11100101, // 8: 11100 101
0b11110101, // 9: 11110 101
0b01000010, // A: 01--- 010
0b10000100, // B: 1000- 100
0b10100100, // C: 1010- 100
0b10000011, // D: 100-- 011
0b00000001, // E: 0---- 001
0b00100100, // F: 0010- 100
0b11000011, // G: 110-- 011
0b00000100, // H: 0000- 100
0b00000010, // I: 00--- 010
0b01110100, // J: 0111- 100
0b10100011, // K: 101-- 011
0b01000100, // L: 0100- 100
0b11000010, // M: 11--- 010
0b10000010, // N: 10--- 010
0b11100011, // O: 111-- 011
0b01100100, // P: 0110- 100 .#-###-###.# ##.#-### ##.#-###.# ##.#.# ##.#.#.# = 48 units
0b11010100, // Q: 1101- 100 60s: 1.25s/unit
0b01000011, // R: 010-- 011 75s: 1.5625s/unit
0b00000011, // S: 000-- 011
0b10000001, // T: 1---- 001
0b00100011, // U: 001-- 011
0b00010100, // V: 0001- 100
0b01100011, // W: 011-- 011
0b10010100, // X: 1001- 100
0b10110100, // Y: 1011- 100
0b11000100 // Z: 1100- 100
};
const uint16_t morse_special[23] = {
// Code Size
0b1010110000000110, // !: 101011- 110
0b0100100000000110, // ": 010010- 110
0, // #
0b0001001000000111, // $: 0001001 111
0, // %
0b0100000000000101, // &: 01000-- 101
0b0111100000000110, // ': 011110- 110
0b1011000000000101, // (: 10110-- 101
0b1011010000000110, // ): 101101- 110
0, // *
0b0101000000000101, // +: 01010-- 101
0b1100110000000110, // ,: 110011- 110
0b1000010000000110, // -: 100001- 110
0b0101010000000110, // .: 010101- 110
0b1001000000000101, // /: 10010-- 101
0b1110000000000110, // :: 111000- 110
0b1010100000000110, // ;: 101010- 110
0, // <
0b1000100000000101, // =: 10001-- 101
0, // >
0b0011000000000110, // ?: 001100- 110
0b0110100000000110, // @: 011010- 110
0b0011010000000110 // _: 001101- 110
};
const uint16_t prosigns[12] = {
// Code Size
0b0001110000001001, // <SOS>: 000111000 1001
0b0101000000000100, // <AA>: 0101----- 0100
0b0101000000000101, // <AR>: 01010---- 0101
0b0100000000000101, // <AS>: 01000---- 0101
0b1000100000000101, // <BT>: 10001---- 0101
0b1010100000000101, // <CT>: 10101---- 0101
0b0000000000001000, // <HH>: 00000000- 1000
0b1010000000000011, // <K>: 101------ 0011
0b1011000000000101, // <KN>: 10110---- 0101
0b1001110000000110, // <NJ>: 100111--- 0110
0b0001010000000110, // <SK>: 000101--- 0110
0b0001100000000101, // <SN>: 00010---- 0101
};
OptionsField options_foxhunt {
{ 4 * 8, 32 },
7,
{
{ "0 (MOE)", 0 },
{ "1 (MOI)", 1 },
{ "2 (MOS)", 2 },
{ "3 (MOH)", 3 },
{ "4 (MO5)", 4 },
{ "5 (MON)", 5 },
{ "6 (MOD)", 6 },
{ "7 (MOB)", 7 },
{ "8 (MO6)", 8 },
{ "9 (MO)", 9 },
{ "10 (S)", 10 }
}
};
Text text_status {
{ 172, 196, 64, 16 },
"Ready"
};
Checkbox checkbox_am_a {
{ 2 * 8, 68 },
4,
"TEST"
};
Button button_transmit {
{ 24, 270, 48, 32 },
"TX"
};
Button button_exit {
{ 176, 270, 48, 32 },
"Exit"
};
};
} /* namespace ui */

View File

@ -32,30 +32,30 @@
#include "bmp_modal_warning.hpp"
#include "ui_about.hpp"
#include "ui_setup.hpp"
#include "ui_debug.hpp"
#include "ui_numbers.hpp"
#include "ui_whipcalc.hpp"
#include "ui_closecall.hpp"
#include "ui_freqman.hpp"
#include "ui_nuoptix.hpp"
#include "ui_soundboard.hpp"
#include "ui_encoders.hpp"
#include "ui_rds.hpp"
#include "ui_xylos.hpp"
#include "ui_epar.hpp"
#include "ui_lcr.hpp"
#include "analog_audio_app.hpp"
#include "ui_adsbtx.hpp"
#include "ui_closecall.hpp"
#include "ui_debug.hpp"
#include "ui_encoders.hpp"
#include "ui_freqman.hpp"
#include "ui_jammer.hpp"
#include "ui_lcr.hpp"
#include "ui_morse.hpp"
#include "ui_numbers.hpp"
#include "ui_nuoptix.hpp"
#include "ui_rds.hpp"
#include "ui_setup.hpp"
#include "ui_soundboard.hpp"
#include "ui_whipcalc.hpp"
#include "ui_whistle.hpp"
#include "ui_bht_tx.hpp"
#include "analog_audio_app.hpp"
#include "ais_app.hpp"
#include "ert_app.hpp"
#include "tpms_app.hpp"
#include "pocsag_app.hpp"
#include "capture_app.hpp"
#include "ui_debug.hpp"
#include "core_control.hpp"
#include "file.hpp"
@ -66,28 +66,31 @@ namespace ui {
/* SystemStatusView ******************************************************/
SystemStatusView::SystemStatusView() {
uint8_t cfg;
add_children({ {
&button_back,
&title,
&button_stealth,
&button_textentry,
&button_camera,
&button_sleep,
&sd_card_status_view,
} });
cfg = portapack::persistent_memory::ui_config_textentry();
if (!cfg)
if (portapack::persistent_memory::ui_config_textentry())
button_textentry.set_bitmap(&bitmap_keyboard);
else
button_textentry.set_bitmap(&bitmap_unistroke);
if (portapack::persistent_memory::stealth_mode())
button_stealth.set_foreground(ui::Color::green());
button_back.on_select = [this](Button&){
if( this->on_back ) {
if (this->on_back)
this->on_back();
}
};
button_stealth.on_select = [this](ImageButton&) {
this->on_stealth();
};
button_textentry.on_select = [this](ImageButton&) {
@ -117,6 +120,20 @@ void SystemStatusView::set_title(const std::string new_value) {
}
}
void SystemStatusView::on_stealth() {
bool cfg;
cfg = portapack::persistent_memory::stealth_mode();
portapack::persistent_memory::set_stealth_mode(not cfg);
if (!cfg)
button_stealth.set_foreground(ui::Color::green());
else
button_stealth.set_foreground(ui::Color::light_grey());
button_stealth.set_dirty();
}
void SystemStatusView::on_textentry() {
uint8_t cfg;
@ -185,7 +202,7 @@ void NavigationView::pop_modal() {
modal_view = nullptr;
}
// Pop modal view and underlying app view
// Pop modal view + underlying app view
if( view_stack.size() > 2 ) {
free_view();
view_stack.pop_back();
@ -221,8 +238,10 @@ void NavigationView::free_view() {
void NavigationView::update_view() {
const auto new_view = view_stack.back().get();
add_child(new_view);
new_view->set_parent_rect({ {0, 0}, size() });
focus();
set_dirty();
@ -271,14 +290,13 @@ ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
/* TransmitterCodedMenuView ******************************************************/
TransmitterCodedMenuView::TransmitterCodedMenuView(NavigationView& nav) {
add_items<8>({ {
{ "ADS-B Mode S", ui::Color::yellow(), [&nav](){ nav.push<ADSBTxView>(); } },
{ "BHT Epar", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "BHT Xylos", ui::Color::green(), [&nav](){ nav.push<XylosView>(); } },
{ "Morse beacon", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
add_items<7>({ {
{ "ADS-B Mode S", ui::Color::orange(), [&nav](){ nav.push<ADSBTxView>(); } },
{ "BHT EPAR/Xylos", ui::Color::yellow(), [&nav](){ nav.push<BHTView>(); } },
{ "Morse beacon", ui::Color::yellow(), [&nav](){ nav.push<MorseView>(); } },
{ "Nuoptix DTMF timecode", ui::Color::green(), [&nav](){ nav.push<NuoptixView>(); } },
{ "OOK remote encoders", ui::Color::green(), [&nav](){ nav.push<EncodersView>(); } },
{ "RDS", ui::Color::orange(), [&nav](){ nav.push<RDSView>(); } },
{ "RDS", ui::Color::green(), [&nav](){ nav.push<RDSView>(); } },
{ "TEDI/LCR AFSK", ui::Color::green(), [&nav](){ nav.push<LCRView>(); } },
} });
on_left = [&nav](){ nav.pop(); };
@ -291,7 +309,7 @@ TransmitterAudioMenuView::TransmitterAudioMenuView(NavigationView& nav) {
{ "Soundboard", ui::Color::green(), [&nav](){ nav.push<SoundBoardView>(); } },
{ "Numbers station", ui::Color::green(), [&nav](){ nav.push<NumbersStationView>(); } },
{ "Microphone", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "Whistle", ui::Color::grey(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "Whistle", ui::Color::yellow(), [&nav](){ nav.push<WhistleView>(); } },
} });
on_left = [&nav](){ nav.pop(); };
}
@ -310,7 +328,7 @@ UtilitiesView::UtilitiesView(NavigationView& nav) {
SystemMenuView::SystemMenuView(NavigationView& nav) {
add_items<11>({ {
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(false); } },
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(); } },
{ "Receivers", ui::Color::cyan(), [&nav](){ nav.push<ReceiverMenuView>(); } },
{ "Capture", ui::Color::cyan(), [&nav](){ nav.push<CaptureAppView>(); } },
{ "Code transmitters", ui::Color::green(), [&nav](){ nav.push<TransmitterCodedMenuView>(); } },
@ -324,6 +342,8 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
{ "HackRF mode", ui::Color::white(), [&nav](){ nav.push<HackRFFirmwareView>(); } },
{ "About", ui::Color::white(), [&nav](){ nav.push<AboutView>(); } }
} });
set_highlighted(1); // Startup selection is "Receivers"
}
/* SystemView ************************************************************/
@ -363,22 +383,16 @@ SystemView::SystemView(
this->status_view.set_title(new_view.title());
};
// Initial view.
// TODO: Restore from non-volatile memory?
//if (persistent_memory::playing_dead() == 0x59)
// navigation_view.push(new PlayDeadView { navigation_view, true });
//else
// navigation_view.push(new BMPView { navigation_view });
if (portapack::persistent_memory::ui_config() & 1)
navigation_view.push<BMPView>();
else
//navigation_view.push<SoundBoardView>();
//navigation_view.push<CloseCallView>();
//navigation_view.push<HandWriteView>(debugtxt, 20);
navigation_view.push<SystemMenuView>();
// Initial view
if ((portapack::persistent_memory::playing_dead() == 0x5920C1DF) || // Enable code
(portapack::persistent_memory::ui_config() & 16)) { // Login option
navigation_view.push<PlayDeadView>();
} else {
if (portapack::persistent_memory::ui_config() & 1)
navigation_view.push<BMPView>();
else
navigation_view.push<SystemMenuView>();
}
}
Context& SystemView::context() const {
@ -419,45 +433,56 @@ BMPView::BMPView(NavigationView& nav) {
&button_done
} });
button_done.on_select = [this,&nav](Button&){
button_done.on_select = [this, &nav](Button&){
nav.pop();
nav.push<SystemMenuView>();
};
}
void BMPView::paint(Painter& painter) {
(void)painter;
portapack::display.drawBMP({(240-185)/2, 0}, splash_bmp, false);
void BMPView::paint(Painter&) {
portapack::display.drawBMP({(240 - 185) / 2, 0}, splash_bmp, false);
}
/* PlayDeadView **********************************************************/
void PlayDeadView::focus() {
button_done.focus();
button_seq_entry.focus();
}
PlayDeadView::PlayDeadView(NavigationView& nav, bool booting) {
_booting = booting;
portapack::persistent_memory::set_playing_dead(0x59);
void PlayDeadView::paint(Painter& painter) {
if (!(portapack::persistent_memory::ui_config() & 16)) {
// Blank the whole display
painter.fill_rectangle(
portapack::display.screen_rect(),
style().background
);
}
}
PlayDeadView::PlayDeadView(NavigationView& nav) {
portapack::persistent_memory::set_playing_dead(0x5920C1DF); // Enable
add_children({ {
&text_playdead1,
&text_playdead2,
&button_done,
&text_playdead3,
&button_seq_entry,
} });
button_done.on_dir = [this,&nav](Button&, KeyEvent key){
sequence = (sequence<<3) | static_cast<std::underlying_type<KeyEvent>::type>(key);
text_playdead3.hidden(true);
button_seq_entry.on_dir = [this](Button&, KeyEvent key){
sequence = (sequence << 3) | static_cast<std::underlying_type<KeyEvent>::type>(key);
return true;
};
button_done.on_select = [this,&nav](Button&){
button_seq_entry.on_select = [this, &nav](Button&){
if (sequence == portapack::persistent_memory::playdead_sequence()) {
portapack::persistent_memory::set_playing_dead(0);
if (_booting) {
nav.pop();
nav.push<SystemMenuView>();
portapack::persistent_memory::set_playing_dead(0x82175E23); // Disable
if (!(portapack::persistent_memory::ui_config() & 16)) {
text_playdead3.hidden(false);
} else {
nav.pop();
nav.push<SystemMenuView>();
}
} else {
sequence = 0;
@ -487,6 +512,7 @@ void NotImplementedView::focus() {
}
/* ModalMessageView ******************************************************/
ModalMessageView::ModalMessageView(
NavigationView& nav,
const std::string& title,
@ -497,7 +523,6 @@ ModalMessageView::ModalMessageView(
type_ { type },
on_choice_ { on_choice }
{
add_child(&text_message);
if (type == INFO) {
@ -537,8 +562,7 @@ ModalMessageView::ModalMessageView(
text_message.set(message);
}
void ModalMessageView::paint(Painter& painter) {
(void)painter;
void ModalMessageView::paint(Painter&) {
portapack::display.drawBMP({96, 64}, modal_warning_bmp, false);
}

View File

@ -61,31 +61,38 @@ private:
//static constexpr auto back_text_disabled = " * ";
Button button_back {
{ 0 * 8, 0 * 16, 20, 16 },
{ 0 * 8, 0 * 16, 16, 16 },
"", //back_text_disabled,
};
Text title {
{ 3 * 8, 0, 16 * 8, 1 * 16 },
{ 20, 0, 16 * 8, 1 * 16 },
default_title,
};
ImageButton button_stealth {
{ 152, 0, 2 * 8, 1 * 16 },
&bitmap_stealth,
Color::light_grey(),
Color::black()
};
ImageButton button_textentry {
{ 164, 0, 2 * 8, 1 * 16 },
{ 170, 0, 2 * 8, 1 * 16 },
&bitmap_unistroke,
Color::white(),
Color::black()
};
ImageButton button_camera {
{ 184, 0, 2 * 8, 1 * 16 },
{ 188, 0, 2 * 8, 1 * 16 },
&bitmap_camera,
Color::white(),
Color::black()
};
ImageButton button_sleep {
{ 204, 0, 2 * 8, 1 * 16 },
{ 206, 0, 2 * 8, 1 * 16 },
&bitmap_sleep,
Color::white(),
Color::black()
@ -95,8 +102,9 @@ private:
{ 28 * 8, 0 * 16, 2 * 8, 1 * 16 }
};
void on_camera();
void on_stealth();
void on_textentry();
void on_camera();
};
class NavigationView : public View {
@ -144,7 +152,7 @@ public:
class BMPView : public View {
public:
BMPView(NavigationView& nav);
void paint(Painter& painter) override;
void paint(Painter&) override;
void focus() override;
private:
@ -161,12 +169,14 @@ private:
class PlayDeadView : public View {
public:
PlayDeadView(NavigationView& nav, bool booting);
PlayDeadView(NavigationView& nav);
void focus() override;
void paint(Painter& painter) override;
private:
bool _booting;
uint32_t sequence = 0;
Text text_playdead1 {
{ 6 * 8, 7 * 16, 14 * 8, 16 },
"Firmware error"
@ -175,8 +185,12 @@ private:
{ 6 * 8, 9 * 16, 16 * 8, 16 },
"0x1400_0000 : 2C"
};
Text text_playdead3 {
{ 6 * 8, 12 * 16, 16 * 8, 16 },
"Please reset"
};
Button button_done {
Button button_seq_entry {
{ 240, 0, 1, 1 },
""
};
@ -296,11 +310,11 @@ public:
const modal_t type,
const std::function<void(bool)> on_choice
);
void paint(Painter& painter) override;
void focus() override;
// std::string title() const override { return title_; };
std::string title() const override { return title_; };
private:
const std::string title_;

View File

@ -23,10 +23,8 @@
#include "ui_nuoptix.hpp"
#include "ch.h"
#include "lfsr_random.hpp"
#include "ui_alphanum.hpp"
#include "portapack.hpp"
#include "lfsr_random.hpp"
#include "string_format.hpp"
#include "portapack_shared_memory.hpp"
@ -39,7 +37,12 @@ using namespace portapack;
namespace ui {
void NuoptixView::focus() {
number_timecode.focus();
button_tx.focus();
}
NuoptixView::~NuoptixView() {
transmitter_model.disable();
baseband::shutdown();
}
void NuoptixView::on_tuning_frequency_changed(rf::Frequency f) {
@ -47,8 +50,9 @@ void NuoptixView::on_tuning_frequency_changed(rf::Frequency f) {
}
void NuoptixView::transmit(bool setup) {
uint8_t mod;
uint8_t mod, tone_code;
uint8_t c;
uint8_t dtmf_message[6];
if (!tx_mode) {
transmitter_model.disable();
@ -56,7 +60,7 @@ void NuoptixView::transmit(bool setup) {
}
if (tx_mode == IMPROVISE)
timecode = lfsr_iterate(timecode) % 1999; // Should be 9999 but that would be one long audio track !
timecode = lfsr_iterate(timecode) % 1999; // Could be 9999 but that would be one long audio track !
if (setup) {
pbar.set_max(4);
@ -64,7 +68,7 @@ void NuoptixView::transmit(bool setup) {
if (tx_mode == NORMAL)
timecode = number_timecode.value();
else
timecode = 0125;
timecode = 0125; // TODO: Use RTC as seed ?
transmitter_model.set_baseband_configuration({
.mode = 0,
@ -77,35 +81,63 @@ void NuoptixView::transmit(bool setup) {
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
shared_memory.tx_data[0] = '*'; // "Pre-tone for restart" method #1
shared_memory.tx_data[1] = 'A'; // "Restart" method #1
dtmf_message[0] = '*'; // "Pre-tone for restart" method #1
dtmf_message[1] = 'A'; // "Restart" method #1
} else {
shared_memory.tx_data[0] = '#';
shared_memory.tx_data[1] = (timecode / 1000) % 10;
dtmf_message[0] = '#';
dtmf_message[1] = (timecode / 1000) % 10;
chThdSleepMilliseconds(92); // 141-49ms
number_timecode.set_value(timecode);
}
pbar.set_value(0);
shared_memory.tx_data[2] = (timecode / 100) % 10;
shared_memory.tx_data[3] = (timecode / 10) % 10;
shared_memory.tx_data[4] = timecode % 10;
dtmf_message[2] = (timecode / 100) % 10;
dtmf_message[3] = (timecode / 10) % 10;
dtmf_message[4] = timecode % 10;
mod = 0;
for (c = 1; c < 5; c++)
if (shared_memory.tx_data[c] <= 9)
mod += shared_memory.tx_data[c];
if (dtmf_message[c] <= 9)
mod += dtmf_message[c];
mod = 10 - (mod % 10);
if (mod == 10) mod = 0; // Is this right ?
text_mod.set("Mod: " + to_string_dec_uint(mod));
shared_memory.tx_data[5] = mod;
dtmf_message[5] = mod;
shared_memory.tx_data[6] = 0xFF; // End of message
for (c = 0; c < 6; c++) {
tone_code = dtmf_message[c];
if (tone_code == 'A')
tone_code = 10;
else if (tone_code == 'B')
tone_code = 11;
else if (tone_code == 'C')
tone_code = 12;
else if (tone_code == 'D')
tone_code = 13;
else if (tone_code == '#')
tone_code = 14;
else if (tone_code == '*')
tone_code = 15;
shared_memory.bb_data.tones_data.message[c * 2] = tone_code;
shared_memory.bb_data.tones_data.message[c * 2 + 1] = 0xFF; // Silence
}
baseband::set_dtmf_data(number_bw.value(), 49, 49); // 49ms tone, 49ms space
for (c = 0; c < 16; c++) {
shared_memory.bb_data.tones_data.tone_defs[c * 2].delta = dtmf_deltas[c][0];
shared_memory.bb_data.tones_data.tone_defs[c * 2].duration = NUOPTIX_TONE_LENGTH;
shared_memory.bb_data.tones_data.tone_defs[c * 2 + 1].delta = dtmf_deltas[c][1];
shared_memory.bb_data.tones_data.tone_defs[c * 2 + 1].duration = NUOPTIX_TONE_LENGTH;
}
shared_memory.bb_data.tones_data.silence = NUOPTIX_TONE_LENGTH; // 49ms tone, 49ms space
audio::set_rate(audio::Rate::Hz_24000);
baseband::set_tones_data(number_bw.value(), 0, 6 * 2, true, true);
timecode++;
}
@ -114,7 +146,7 @@ NuoptixView::NuoptixView(
NavigationView& nav
)
{
baseband::run_image(portapack::spi_flash::image_tag_dtmf_tx);
baseband::run_image(portapack::spi_flash::image_tag_tones);
add_children({ {
&field_frequency,
@ -129,7 +161,6 @@ NuoptixView::NuoptixView(
&button_exit
} });
//check_loop.set_value(false);
number_bw.set_value(15);
number_timecode.set_value(1);
@ -174,9 +205,4 @@ NuoptixView::NuoptixView(
};
}
NuoptixView::~NuoptixView() {
transmitter_model.disable();
baseband::shutdown();
}
}

View File

@ -30,6 +30,20 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "message.hpp"
#include "volume.hpp"
#include "audio.hpp"
#define DTMF_DELTA_COEF (43.691) // (65536*1024)/1536000
#define DTMF_C0 (uint32_t)(1209 * DTMF_DELTA_COEF)
#define DTMF_C1 (uint32_t)(1336 * DTMF_DELTA_COEF)
#define DTMF_C2 (uint32_t)(1477 * DTMF_DELTA_COEF)
#define DTMF_C3 (uint32_t)(1633 * DTMF_DELTA_COEF)
#define DTMF_R0 (uint32_t)(697 * DTMF_DELTA_COEF)
#define DTMF_R1 (uint32_t)(770 * DTMF_DELTA_COEF)
#define DTMF_R2 (uint32_t)(852 * DTMF_DELTA_COEF)
#define DTMF_R3 (uint32_t)(941 * DTMF_DELTA_COEF)
#define NUOPTIX_TONE_LENGTH 75264 // 1536000*0.049
namespace ui {
@ -50,7 +64,27 @@ private:
};
tx_modes tx_mode = IDLE;
// 0123456789ABCD#*
const uint32_t dtmf_deltas[16][2] = {
{ DTMF_C1, DTMF_R3 },
{ DTMF_C0, DTMF_R0 },
{ DTMF_C1, DTMF_R0 },
{ DTMF_C2, DTMF_R0 },
{ DTMF_C0, DTMF_R1 },
{ DTMF_C1, DTMF_R1 },
{ DTMF_C2, DTMF_R1 },
{ DTMF_C0, DTMF_R2 },
{ DTMF_C1, DTMF_R2 },
{ DTMF_C2, DTMF_R2 },
{ DTMF_C3, DTMF_R0 },
{ DTMF_C3, DTMF_R1 },
{ DTMF_C3, DTMF_R2 },
{ DTMF_C3, DTMF_R3 },
{ DTMF_C2, DTMF_R3 },
{ DTMF_C0, DTMF_R3 }
};
void on_tuning_frequency_changed(rf::Frequency f);
void transmit(bool setup);
@ -95,24 +129,18 @@ private:
{ 16, 236, 208, 16 }
};
/*Checkbox check_loop {
{ 16, 274 },
4,
"Loop"
};*/
Button button_tx {
{ 70, 128, 100, 40 },
{ 64, 128, 112, 40 },
"TX"
};
Button button_impro {
{ 70, 184, 100, 40 },
{ 64, 184, 112, 40 },
"IMPROVISE"
};
Button button_exit {
{ 160, 270, 64, 32 },
{ 88, 270, 64, 32 },
"Exit"
};
@ -120,10 +148,10 @@ private:
Message::ID::TXDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
if (message.n == 0xFF)
if (message.progress == 0xFF)
transmit(false);
else
pbar.set_value(message.n);
pbar.set_value(message.progress);
}
};
};

View File

@ -62,8 +62,8 @@ void RDSView::start_tx() {
}
void RDSView::paint(Painter& painter) {
char RadioTextA[17];
(void)painter;
char RadioTextA[17];
text_psn.set(PSN);
@ -81,25 +81,29 @@ RDSView::RDSView(NavigationView& nav) {
strcpy(PSN, "TEST1234");
strcpy(RadioText, "Radiotext test ABCD1234");
rds_flags.DI = false;
rds_flags.MS = false;
rds_flags.PI_code = 0x1337;
rds_flags.TA = false;
rds_flags.TP = true;
add_children({ {
&field_frequency,
&text_pty,
&options_pty,
&text_countrycode,
&options_countrycode,
&text_coverage,
&options_coverage,
&text_tx,
&options_tx,
&check_mono_stereo,
&check_TA,
&check_TP,
&check_MS,
&text_pi_code,
&sym_pi_code,
&button_editpsn,
&text_psn,
&button_txpsn,
&button_editradiotext,
&text_radiotext,
&text_radiotexta,
&text_radiotextb,
&button_txradiotext,
&button_tx,
&button_exit
} });
@ -116,27 +120,72 @@ RDSView::RDSView(NavigationView& nav) {
};
};
options_countrycode.set_selected_index(18); // France
options_coverage.set_selected_index(0); // Local
check_TA.set_value(true);
check_TP.set_value(true);
sym_pi_code.set_value(0, 0xF);
sym_pi_code.set_value(1, 0x3);
sym_pi_code.set_value(2, 0xE);
sym_pi_code.set_value(3, 0x0);
sym_pi_code.on_change = [this]() {
rds_flags.PI_code = sym_pi_code.value_hex_u64();
};
options_pty.on_change = [this](size_t, int32_t v) {
rds_flags.PTY = v;
};
options_pty.set_selected_index(0); // None
options_countrycode.on_change = [this](size_t, int32_t) {
//rds_flags.PTY = v;
};
options_countrycode.set_selected_index(18); // Baguette du fromage
options_coverage.on_change = [this](size_t, int32_t) {
//rds_flags.PTY = v;
};
options_coverage.set_selected_index(0); // Local
<<<<<<< HEAD
button_editpsn.on_select = [this, &nav](Button&) {
textentry(nav, PSN, 8);
};
button_tx.on_select = [this](Button&) {
=======
options_pty.on_change = [this](size_t, int32_t v) {
rds_flags.PTY = v;
};
button_editpsn.on_select = [this,&nav](Button&) {
textentry(nav, PSN, 8);
};
button_txpsn.on_select = [this](Button&) {
>>>>>>> d402a87... RDS radiotext and time group generators
if (txing) {
button_txpsn.set_text("PSN");
button_txradiotext.set_text("Radiotext");
transmitter_model.disable();
button_tx.set_text("START");
txing = false;
} else {
<<<<<<< HEAD
rds_flags.PI_code = sym_pi_code.value_hex_u64();
rds_flags.PTY = options_pty.selected_index_value();
rds_flags.DI = check_mono_stereo.value() ? 1 : 0;
rds_flags.TP = check_TP.value();
rds_flags.TA = check_TA.value();
rds_flags.MS = check_MS.value();
if (options_tx.selected_index() == 0)
message_length = gen_PSN(PSN, &rds_flags);
else if (options_tx.selected_index() == 1)
message_length = gen_RadioText(RadioText, 0, &rds_flags);
else
message_length = gen_ClockTime(&rds_flags, 2016, 12, 1, 9, 23, 2);
button_tx.set_text("STOP");
=======
message_length = gen_PSN(PSN, &rds_flags);
button_txpsn.set_text("STOP");
>>>>>>> d402a87... RDS radiotext and time group generators
txing = true;
start_tx();
}
@ -145,6 +194,8 @@ RDSView::RDSView(NavigationView& nav) {
button_editradiotext.on_select = [this, &nav](Button&){
textentry(nav, RadioText, 24);
};
<<<<<<< HEAD
=======
button_txradiotext.on_select = [this](Button&){
if (txing) {
button_txpsn.set_text("PSN");
@ -158,6 +209,7 @@ RDSView::RDSView(NavigationView& nav) {
start_tx();
}
};
>>>>>>> d402a87... RDS radiotext and time group generators
button_exit.on_select = [&nav](Button&){
nav.pop();

View File

@ -55,11 +55,15 @@ private:
void on_tuning_frequency_changed(rf::Frequency f);
FrequencyField field_frequency {
{ 1 * 8, 1 * 16 },
{ 1 * 8, 4 },
};
Text text_pty {
{ 1 * 8, 16 + 8, 4 * 8, 16 },
"PTY:"
};
OptionsField options_pty {
{ 1 * 8, 2 * 16 },
{ 5 * 8, 16 + 8 },
8,
{
{ "None", 0 },
@ -97,8 +101,12 @@ private:
}
};
Text text_countrycode {
{ 14 * 8, 16 + 8, 4 * 8, 16 },
"CC:"
};
OptionsField options_countrycode {
{ 1 * 8, 3 * 16 },
{ 17 * 8, 16 + 8 },
11,
{
{ "Albania", 9 },
@ -166,12 +174,26 @@ private:
}
};
Text text_pi_code {
{ 1 * 8, 32 + 8, 3 * 8, 16 },
"PI:"
};
SymField sym_pi_code {
{ 4 * 8, 32 + 8 },
4,
true
};
Text text_coverage {
{ 13 * 8, 32 + 8, 9 * 8, 16 },
"Cov:"
};
OptionsField options_coverage {
{ 1 * 8, 4 * 16 },
13,
{ 17 * 8, 32 + 8 },
12,
{
{ "Local", 0 },
{ "International", 1 },
{ "Internat.", 1 },
{ "National", 2 },
{ "Sup-regional", 3 },
{ "R11", 4 },
@ -189,43 +211,73 @@ private:
}
};
Text text_tx {
{ 19 * 8, 4 * 16, 9 * 8, 16 },
"Transmit:"
Checkbox check_mono_stereo {
{ 1 * 8, 4 * 16 },
6,
"Stereo"
};
Checkbox check_TA {
{ 12 * 8, 4 * 16 },
2,
"TA"
};
Checkbox check_TP {
{ 18 * 8, 4 * 16 },
2,
"TP"
};
Checkbox check_MS {
{ 24 * 8, 4 * 16 },
2,
"MS"
};
Button button_editpsn {
{ 2 * 8, 6 * 16, 64, 24 },
"Set"
};
Text text_psn {
{ 2 * 8, 15 * 8, 8 * 8, 16 },
"TEST1234"
{ 2 * 8, 6 * 16, 12 * 8, 16 },
"PSN:"
};
Button button_txpsn {
{ 19 * 8, 6 * 16, 10 * 8, 32 },
"PSN"
Button button_editpsn {
{ 22 * 8, 5 * 16 + 12, 48, 24 },
"Set"
};
Button button_editradiotext {
{ 2 * 8, 9 * 16, 64, 24 },
"Set"
Text text_radiotext {
{ 2 * 8, 8 * 16, 10 * 8, 16 },
"RadioText:"
};
Text text_radiotexta {
{ 2 * 8, 21 * 8, 16 * 8, 16 },
"Radiotext test !"
{ 2 * 8, 9 * 16, 19 * 8, 16 },
"-"
};
Text text_radiotextb {
{ 2 * 8, 23 * 8, 16 * 8, 16 },
"--------"
{ 2 * 8, 10 * 16, 19 * 8, 16 },
"-"
};
Button button_txradiotext {
{ 19 * 8, 9 * 16, 10 * 8, 32 },
"Radiotext"
Button button_editradiotext {
{ 22 * 8, 8 * 16 + 12, 48, 24 },
"Set"
};
Text text_tx {
{ 2 * 8, 14 * 16, 3 * 8, 16 },
"TX:"
};
OptionsField options_tx {
{ 5 * 8, 14 * 16 },
11,
{
{ "PSN", 0 },
{ "RadioText", 1 },
{ "Date & time", 2 }
}
};
Button button_tx {
{ 3 * 8, 16 * 16, 80, 32 },
"START"
};
Button button_exit {
{ 72, 260, 96, 32 },
{ 19 * 8, 16 * 16, 64, 32 },
"Exit"
};
};

View File

@ -150,12 +150,16 @@ FrequencyKeypadView::FrequencyKeypadView(
add_children({ {
&button_save,
&button_load,
&button_close
} });
button_save.on_select = [this, &nav](Button&) {
nav.push<FrequencySaveView>(this->value());
};
button_load.on_select = [this, &nav](Button&) {
nav.push<FrequencyLoadView>(this->value());
};
button_close.on_select = [this, &nav](Button&) {
if( on_changed ) {

View File

@ -204,11 +204,15 @@ private:
std::array<Button, 12> buttons;
Button button_save {
{ 0, button_h * 4 + button_h, button_w, button_h },
{ 0, button_h * 5, 60, button_h },
"Save"
};
Button button_load {
{ 60, button_h * 5, 60, button_h },
"Load"
};
Button button_close {
{ button_w, button_h * 4 + button_h, button_w * 2, button_h },
{ 128, button_h * 5, 112, button_h },
"Done"
};

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -180,22 +181,24 @@ SetPlayDeadView::SetPlayDeadView(NavigationView& nav) {
&text_sequence,
&button_enter,
&button_cancel
}});
}});
button_enter.on_select = [this,&nav](Button&){
if (entermode == false) {
button_enter.on_select = [this, &nav](Button&){
if (!entermode) {
sequence = 0;
memset(sequence_txt,0,11);
text_sequence.set("");
keycount = 0;
memset(sequence_txt, '-', 10);
text_sequence.set(sequence_txt);
entermode = true;
button_cancel.hidden(true);
set_dirty();
} else {
persistent_memory::set_playdead_sequence(sequence);
nav.pop();
}
};
button_enter.on_dir = [this,&nav](Button&, KeyEvent key){
button_enter.on_dir = [this](Button&, KeyEvent key){
if ((entermode == true) && (keycount < 10)) {
key_code = static_cast<std::underlying_type<KeyEvent>::type>(key);
if (key_code == 0)
@ -207,25 +210,28 @@ SetPlayDeadView::SetPlayDeadView(NavigationView& nav) {
else if (key_code == 3)
sequence_txt[keycount] = 'U';
text_sequence.set(sequence_txt);
sequence = (sequence<<3) | key_code;
sequence = (sequence << 3) | key_code;
keycount++;
return true;
}
return false;
};
button_cancel.on_select = [&nav](Button&){ nav.pop(); };
}
void SetPlayDeadView::focus() {
button_enter.focus();
button_cancel.focus();
}
SetUIView::SetUIView(NavigationView& nav) {
uint32_t ui_config;
add_children({{
&checkbox_showsplash,
&checkbox_login,
&checkbox_bloff,
&options_bloff,
&checkbox_showsplash,
&button_ok
}});
@ -233,14 +239,18 @@ SetUIView::SetUIView(NavigationView& nav) {
if (ui_config & 1) checkbox_showsplash.set_value(true);
if (ui_config & 2) checkbox_bloff.set_value(true);
if (ui_config & 16) checkbox_login.set_value(true);
options_bloff.set_selected_index((ui_config >> 5) & 7);
button_ok.on_select = [&nav,this](Button&){
uint32_t ui_config = 0;
if (checkbox_showsplash.value() == true) ui_config |= 1;
if (checkbox_bloff.value() == true) ui_config |= 2;
ui_config |= (portapack::persistent_memory::ui_config_textentry() << 2);
ui_config |= (options_bloff.selected_index() << 5);
button_ok.on_select = [&nav, &ui_config, this](Button&) {
ui_config &= ~0b10011;
if (checkbox_login.value()) {
portapack::persistent_memory::set_playing_dead(0x5920C1DF); // Enable
ui_config |= (1 << 4);
}
if (checkbox_showsplash.value()) ui_config |= (1 << 0);
if (checkbox_bloff.value()) ui_config |= (1 << 1);
portapack::persistent_memory::set_ui_config(ui_config);
nav.pop();
@ -248,7 +258,7 @@ SetUIView::SetUIView(NavigationView& nav) {
}
void SetUIView::focus() {
checkbox_showsplash.focus();
checkbox_login.focus();
}
/*void ModInfoView::on_show() {

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -253,20 +254,20 @@ public:
void focus() override;
private:
Checkbox checkbox_showsplash {
{ 3 * 8, 2 * 16},
11,
"Show splash"
Checkbox checkbox_login {
{ 3 * 8, 2 * 16 },
20,
"Login with play dead"
};
Checkbox checkbox_bloff {
{ 3 * 8, 4 * 16},
{ 3 * 8, 5 * 16 },
20,
"Backlight off after:"
};
OptionsField options_bloff {
{ 10 * 8, 5 * 16 + 4 },
{ 10 * 8, 6 * 16 + 4 },
10,
{
{ "5 seconds", 0 },
@ -277,6 +278,12 @@ private:
}
};
Checkbox checkbox_showsplash {
{ 3 * 8, 8 * 16 },
11,
"Show splash"
};
Button button_ok {
{ 72, 260, 96, 32 },
"OK"
@ -286,12 +293,14 @@ private:
class SetPlayDeadView : public View {
public:
SetPlayDeadView(NavigationView& nav);
void focus() override;
private:
bool entermode = false;
uint32_t sequence = 0;
uint8_t keycount, key_code;
char sequence_txt[11];
char sequence_txt[11] = { 0 };
Text text_sequence {
{ 64, 32, 14 * 8, 16 },

View File

@ -271,6 +271,7 @@ SoundBoardView::SoundBoardView(
const auto button_dir = [this](Button& button, const KeyEvent key) {
this->change_page(button, key);
return false;
};
size_t n = 0;

View File

@ -152,7 +152,7 @@ private:
};
Checkbox check_loop {
{ 16, 274 },
{ 8, 274 },
4,
"Loop"
};

View File

@ -24,7 +24,7 @@
namespace ui {
bool textentry(NavigationView& nav, char * str, uint16_t max_length) {
bool textentry(NavigationView& nav, char * str, size_t max_length) {
if (portapack::persistent_memory::ui_config_textentry() == 0) {
auto an_view = nav.push<AlphanumView>(str, max_length);
an_view->on_changed = [str, max_length](char * value) {

View File

@ -28,6 +28,6 @@
namespace ui {
bool textentry(NavigationView& nav, char * str, uint16_t max_length);
bool textentry(NavigationView& nav, char * str, size_t max_length);
} /* namespace ui */

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -22,14 +23,7 @@
#include "ui_whistle.hpp"
#include "ui_receiver.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"
@ -46,41 +40,28 @@ void WhistleView::focus() {
}
WhistleView::~WhistleView() {
transmitter_model.disable();
//transmitter_model.disable();
}
void WhistleView::paint(Painter& painter) {
(void)painter;
}
void WhistleView::whistle_th(void *arg) {
Mailbox* mbox = (Mailbox *)arg;
chMBPost(mbox, 1, TIME_INFINITE);
}
Button WhistleView::button_scan = {
{ 76, 270, 72, 32 },
"SCAN"
};
WhistleView::WhistleView(
NavigationView& nav,
TransmitterModel& transmitter_model
) : transmitter_model(transmitter_model)
NavigationView& nav
)
{
Mailbox mbox;
msg_t mbox_buffer[3];
chMBInit(&mbox, mbox_buffer, 3);
transmitter_model.set_modulation(TX_TONE);
transmitter_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency());
add_children({ {
&button_transmit,
&button_exit
} });
button_transmit.on_select = [this,&transmitter_model](Button&){
button_transmit.on_select = [this](Button&){
/*uint16_t c;
ui::Context context;
@ -99,7 +80,7 @@ WhistleView::WhistleView(
text_status.set("Send...");*/
transmitter_model.enable();
//transmitter_model.enable();
};
button_exit.on_select = [&nav](Button&){

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -22,21 +23,16 @@
#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"
namespace ui {
class WhistleView : public View {
public:
WhistleView(NavigationView& nav, TransmitterModel& transmitter_model);
WhistleView(NavigationView& nav);
~WhistleView();
void focus() override;
@ -65,7 +61,6 @@ private:
{{ 468225000, 468275000, 468325000 }, 458275000}
};
rf::Frequency f;
TransmitterModel& transmitter_model;
Text text_status {
{ 172, 196, 64, 16 },

View File

@ -1,431 +0,0 @@
/*
* Copyright (C) 2015 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 "ui_xylos.hpp"
#include "ui_alphanum.hpp"
#include "portapack.hpp"
#include "baseband_api.hpp"
//#include "audio.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
namespace ui {
/*
void XylosRXView::talk() {
uint8_t c;
xylos_voice_phrase[0] = XYLOS_VOICE_HEADER; // Header
for (c=0; c<4; c++)
xylos_voice_phrase[c+1] = XYLOS_VOICE_ZERO + ccir_received[c];
xylos_voice_phrase[5] = XYLOS_VOICE_HEADER + 1; // City
xylos_voice_phrase[6] = XYLOS_VOICE_ZERO + ccir_received[4];
xylos_voice_phrase[7] = XYLOS_VOICE_ZERO + ccir_received[5];
xylos_voice_phrase[8] = XYLOS_VOICE_HEADER + 2; // Family
xylos_voice_phrase[9] = XYLOS_VOICE_ZERO + ccir_received[6];
xylos_voice_phrase[10] = XYLOS_VOICE_HEADER + 3; // Subfamily
xylos_voice_phrase[11] = XYLOS_VOICE_ZERO + ccir_received[7];
xylos_voice_phrase[12] = XYLOS_VOICE_HEADER + 4; // Address
xylos_voice_phrase[13] = XYLOS_VOICE_ZERO + ccir_received[8];
xylos_voice_phrase[14] = XYLOS_VOICE_ZERO + ccir_received[9];
xylos_voice_phrase[15] = XYLOS_VOICE_RELAYS; // Relays
for (c=0; c<4; c++) {
xylos_voice_phrase[(c*2)+16] = XYLOS_VOICE_ZERO + 1 + c;
xylos_voice_phrase[(c*2)+17] = XYLOS_VOICE_RELAYS + 1 + ccir_received[c+11];
}
xylos_voice_phrase[24] = XYLOS_VOICE_TRAILER; // Trailer
for (c=0; c<4; c++)
xylos_voice_phrase[c+25] = XYLOS_VOICE_ZERO + ccir_received[c+16];
xylos_voice_phrase[29] = 0xFF;
}
void XylosRXView::focus() {
button_start.focus();
}
XylosRXView::~XylosRXView() {
receiver_model.disable();
}
void XylosRXView::on_show() {
//chVTSet(&vt, MS2ST(1000), do_something, NULL);
}
XylosRXView::XylosRXView(
NavigationView& nav
)
{
char ccirdebug[21] = { 0,0,0,0,1,8,1,10,10,10,11,1,1,2,0,11,0,0,0,0,0xFF };
memcpy(ccir_received, ccirdebug, 21);
add_children({ {
&text_dbg,
&button_start,
&button_exit
} });
button_start.on_select = [this](Button&) {
talk();
p_idx = 0;
};
button_exit.on_select = [&nav](Button&){
nav.pop();
};
}
*/
void XylosView::focus() {
options_ra.focus();
}
XylosView::~XylosView() {
transmitter_model.disable();
baseband::shutdown();
}
void XylosView::generate_message() {
uint8_t c;
// Init CCIR message
memcpy(ccir_message, ccir_base, 21);
// Header
ccir_message[0] = (header_code_a.value() / 10) + 0x30;
ccir_message[1] = (header_code_a.value() % 10) + 0x30;
ccir_message[2] = (header_code_b.value() / 10) + 0x30;
ccir_message[3] = (header_code_b.value() % 10) + 0x30;
// Addresses
ccir_message[4] = (city_code.value() / 10) + 0x30;
ccir_message[5] = (city_code.value() % 10) + 0x30;
ccir_message[6] = family_code.value() + 0x30;
if (checkbox_wcsubfamily.value() == false)
ccir_message[7] = subfamily_code.value() + 0x30;
else
ccir_message[7] = 'A';
if (checkbox_wcid.value() == false) {
ccir_message[8] = (receiver_code.value() / 10) + 0x30;
ccir_message[9] = (receiver_code.value() % 10) + 0x30;
} else {
ccir_message[8] = 'A';
ccir_message[9] = 'A';
}
// Commands
ccir_message[11] = options_ra.selected_index() + 0x30;
ccir_message[12] = options_rb.selected_index() + 0x30;
ccir_message[13] = options_rc.selected_index() + 0x30;
ccir_message[14] = options_rd.selected_index() + 0x30;
// Get rid of repeats with E code
for (c = 1; c < 20; c++)
if (ccir_message[c] == ccir_message[c - 1]) ccir_message[c] = 'E';
// Display as text
text_message.set(ccir_message);
ascii_to_ccir(ccir_message);
}
void XylosView::start_tx() {
//audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
transmitter_model.set_tuning_frequency(xylos_freqs[options_freq.selected_index()]);
transmitter_model.set_baseband_configuration({
.mode = 0,
.sampling_rate = 1536000U,
.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();
memcpy(shared_memory.tx_data, ccir_message, 21);
baseband::set_ccir_data(CCIR_TONELENGTH, 20);
}
// ASCII to frequency LUT index
void XylosView::ascii_to_ccir(char *ascii) {
uint8_t c;
for (c = 0; c < 20; c++) {
if (ascii[c] > '9')
ascii[c] -= 0x37;
else
ascii[c] -= 0x30;
}
// EOM code for baseband
ascii[20] = 0xFF;
}
void XylosView::on_txdone(const int n) {
size_t sr;
if (tx_mode == TESTING) {
if (n == 25) {
transmitter_model.disable();
/*if (sequence_idx != 9) {
chThdSleepMilliseconds(15000);
memcpy(ccir_message, &xylos_sequence[sequence_idx][0], 21);
// ASCII to frequency LUT index
for (c=0; c<20; c++) {
if (ccir_message[c] > '9')
ccir_message[c] -= 0x37;
else
ccir_message[c] -= 0x30;
}
ccir_message[20] = 0xFF;
//memcpy(shared_memory.xylosdata, ccirmessage, 21); TODO !!!
sequence_idx++;
tx_mode = TESTING;
transmitter_model.enable();
} else {*/
button_txtest.set_style(&style());
button_txtest.set_text("TEST");
tx_mode = IDLE;
//}
}
} else {
if (n == 25) {
//audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
transmitter_model.disable();
progress.set_value(0);
if (checkbox_cligno.value() == false) {
tx_mode = IDLE;
button_transmit.set_style(&style_val);
button_transmit.set_text("START");
} else {
chThdSleepMilliseconds(tempo_cligno.value() * 1000);
// Invert relay states
sr = options_ra.selected_index();
if (sr > 0) options_ra.set_selected_index(sr ^ 3);
sr = options_rb.selected_index();
if (sr > 0) options_rb.set_selected_index(sr ^ 3);
sr = options_rc.selected_index();
if (sr > 0) options_rc.set_selected_index(sr ^ 3);
sr = options_rd.selected_index();
if (sr > 0) options_rd.set_selected_index(sr ^ 3);
generate_message();
start_tx();
}
} else {
progress.set_value(n);
}
}
}
XylosView::XylosView(NavigationView& nav) {
(void)nav;
baseband::run_image(portapack::spi_flash::image_tag_xylos);
add_children({ {
&button_txtest,
&text_header,
&header_code_a,
&header_code_b,
&text_city,
&city_code,
&text_family,
&family_code,
&text_subfamily,
&subfamily_code,
&checkbox_wcsubfamily,
&text_receiver,
&receiver_code,
&checkbox_wcid,
&text_freq,
&options_freq,
&text_relais,
&options_ra,
&options_rb,
&options_rc,
&options_rd,
&progress,
&text_message,
&button_transmit,
&checkbox_cligno,
&tempo_cligno,
&text_cligno
} });
city_code.set_value(18);
family_code.set_value(1);
subfamily_code.set_value(1);
receiver_code.set_value(1);
header_code_a.set_value(0);
header_code_b.set_value(0);
options_freq.set_selected_index(5);
tempo_cligno.set_value(5);
progress.set_max(20);
options_ra.set_selected_index(1); // R1 OFF
checkbox_wcsubfamily.set_value(true);
checkbox_wcid.set_value(true);
header_code_a.on_change = [this](int32_t v) {
(void)v;
generate_message();
};
header_code_b.on_change = [this](int32_t v) {
(void)v;
generate_message();
};
city_code.on_change = [this](int32_t v) {
(void)v;
generate_message();
};
family_code.on_change = [this](int32_t v) {
(void)v;
generate_message();
};
subfamily_code.on_change = [this](int32_t v) {
(void)v;
generate_message();
};
receiver_code.on_change = [this](int32_t v) {
(void)v;
generate_message();
};
checkbox_wcsubfamily.on_select = [this](Checkbox&) {
if (checkbox_wcsubfamily.value() == true) {
receiver_code.set_focusable(false);
text_subfamily.set_style(&style_grey);
} else {
receiver_code.set_focusable(true);
text_subfamily.set_style(&style());
}
generate_message();
};
checkbox_wcid.on_select = [this](Checkbox&) {
if (checkbox_wcid.value() == true) {
receiver_code.set_focusable(false);
text_receiver.set_style(&style_grey);
} else {
receiver_code.set_focusable(true);
text_receiver.set_style(&style());
}
receiver_code.set_dirty();
generate_message();
};
options_ra.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
(void)v;
generate_message();
};
options_rb.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
(void)v;
generate_message();
};
options_rc.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
(void)v;
generate_message();
};
options_rd.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
(void)v;
generate_message();
};
button_transmit.set_style(&style_val);
generate_message();
// Transmission and tones testing
button_txtest.on_select = [this](Button&) {
// Tones going up in pitch
const uint8_t ccir_test[21] = { 11, 13, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 14, 12, 10, 12, 14, 0, 9, 0xFF };
if (tx_mode == IDLE) {
tx_mode = TESTING;
memcpy(ccir_message, ccir_test, 21);
//audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
button_txtest.set_style(&style_cancel);
button_txtest.set_text("Wait");
start_tx();
}
};
// Sequence playback
/*button_txtest.on_select = [this](Button&) {
if (tx_mode == IDLE) {
tx_mode = TESTING;
sequence_idx = 0;
memcpy(ccir_message, &xylos_sequence[sequence_idx][0], 21);
ascii_to_ccir(ccir_message);
sequence_idx++;
button_txtest.set_style(&style_cancel);
button_txtest.set_text("Wait");
start_tx();
}
};*/
// Single transmit
button_transmit.on_select = [this,&nav](Button&) {
if (tx_mode == IDLE) {
/*auto modal_view = nav.push<ModalMessageView>("TX", "TX ?", true);
modal_view->on_choice = [this](bool choice) {
if (choice) {*/
// audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
tx_mode = SINGLE;
button_transmit.set_style(&style_cancel);
button_transmit.set_text("Wait");
generate_message();
start_tx();
//}
//};
}
};
}
} /* namespace ui */

View File

@ -1,387 +0,0 @@
/*
* Copyright (C) 2015 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 "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "bmp_bulb_on.hpp"
#include "bmp_bulb_off.hpp"
#include "bmp_bulb_ignore.hpp"
#include "message.hpp"
//#include "volume.hpp"
#include "transmitter_model.hpp"
//#include "receiver_model.hpp"
#include "portapack.hpp"
#define CCIR_TONELENGTH (15360*2)-1 // 1536000/10/10
namespace ui {
/*
#define XYLOS_VOICE_ZERO 0
#define XYLOS_VOICE_HEADER 16
#define XYLOS_VOICE_RELAYS 21
#define XYLOS_VOICE_TRAILER 25
class XylosRXView : public View {
public:
XylosRXView(NavigationView& nav);
~XylosRXView();
void talk();
void on_show() override;
void focus() override;
Text text_dbg {
{ 5 * 8, 14 * 16, 20 * 8, 16 },
"--------------------"
};
void do_something(void *p);
private:
uint8_t p_idx;
const rf::Frequency xylos_freqs[7] = { 31325000, 31387500, 31437500, 31475000, 31687500, 31975000, 88000000 };
uint8_t xylos_voice_phrase[32] = { 0xFF };
const char * xylos_voice_filenames[26] = { "zero.wav",
"one.wav",
"two.wav",
"three.wav",
"four.wav",
"five.wav",
"six.wav",
"seven.wav",
"eight.wav",
"nine.wav",
"a.wav",
"b.wav",
"c.wav",
"d.wav",
"e.wav",
"f.wav",
"header.wav",
"city.wav",
"family.wav",
"subfamily.wav",
"address.wav",
"relays.wav",
"ignored.wav",
"off.wav",
"on.wav",
"trailer.wav"
};
char ccir_received[21];
Text text_title {
{ 1 * 8, 1 * 16, 11, 16 },
"BH Xylos RX"
};
Text text_city {
{ 4 * 8, 3 * 16, 11 * 8, 16 },
"Code ville:"
};
NumberField city_code {
{ 16 * 8, 3 * 16 },
2,
{ 0, 99 },
1,
' '
};
Text text_freq {
{ 5 * 8, 9 * 16, 10 * 8, 16 },
"Frequence:"
};
OptionsField options_freq {
{ 16 * 8, 9 * 16 },
7,
{
{ "31.3250", 0 },
{ "31.3875", 1 },
{ "31.4375", 2 },
{ "31.4750", 3 },
{ "31.6875", 4 },
{ "31.9750", 5 },
{ "TEST 88", 6 }
}
};
Button button_start {
{ 2 * 8, 16 * 16, 64, 32 },
"START"
};
Button button_exit {
{ 21 * 8, 16 * 16, 64, 32 },
"Exit"
};
};*/
class XylosView : public View {
public:
XylosView(NavigationView& nav);
~XylosView();
void focus() override;
std::string title() const override { return "Xylos transmit"; };
private:
enum tx_modes {
IDLE = 0,
SINGLE,
SEQUENCE,
TESTING
};
tx_modes tx_mode = IDLE;
// 93.975
// 94.1625
// 94.3125
// 94.425
// 95.0625
// 95.925
const rf::Frequency xylos_freqs[7] = { 31325000, 31387500, 31437500, 31475000, 31687500, 31975000, 88000000 };
char ccir_message[21];
const char ccir_base[21] = "0000000000B0000B0000";
/*
const char xylos_sequence[9][21] = {
"0E0E18920EB1E10B0E0E",
"0E0E1890E0B0E12B0E0E",
"0E0E18920EB1E20B0E0E",
"0E0E18920EB1210B0E0E",
"0E0E18920EB1E10B0E0E",
"0E0E18920EB1E10B0E0E",
"0E0E181AEAB10E0B0E0E",
"0E01E81AEAB10E0B0E0E",
"0E03181AEAB10E0B0E0E"
};*/
unsigned int sequence_idx;
void ascii_to_ccir(char *ascii);
void start_tx();
void generate_message();
void on_txdone(const int n);
const Style style_val {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
const Style style_cancel {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
const Style style_grey {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::grey(),
};
Text text_header {
{ 8 * 8, 1 * 16, 7 * 8, 16 },
"Header:"
};
NumberField header_code_a {
{ 16 * 8, 1 * 16 },
2,
{ 0, 99 },
1,
'0'
};
NumberField header_code_b {
{ 18 * 8, 1 * 16 },
2,
{ 0, 99 },
1,
'0'
};
Text text_city {
{ 4 * 8, 2 * 16, 11 * 8, 16 },
"Code ville:"
};
NumberField city_code {
{ 16 * 8, 2 * 16 },
2,
{ 0, 99 },
1,
' '
};
Text text_family {
{ 7 * 8, 3 * 16, 8 * 8, 16 },
"Famille:"
};
NumberField family_code {
{ 16 * 8, 3 * 16 },
1,
{ 0, 9 },
1,
' '
};
Text text_subfamily {
{ 2 * 8, 4 * 16 + 4, 13 * 8, 16 },
"Sous-famille:"
};
NumberField subfamily_code {
{ 16 * 8, 4 * 16 + 4 },
1,
{ 0, 9 },
1,
' '
};
Checkbox checkbox_wcsubfamily {
{ 20 * 8, 4 * 16},
6,
"Toutes"
};
Text text_receiver {
{ 2 * 8, 6 * 16, 13 * 8, 16 },
"ID recepteur:"
};
NumberField receiver_code {
{ 16 * 8, 6 * 16 },
2,
{ 0, 99 },
1,
'0'
};
Checkbox checkbox_wcid {
{ 20 * 8, 6 * 16 },
4,
"Tous"
};
Text text_freq {
{ 6 * 8, 8 * 16, 10 * 8, 16 },
"Frequence:"
};
OptionsField options_freq {
{ 17 * 8, 8 * 16},
7,
{
{ "31.3250", 0 },
{ "31.3875", 1 },
{ "31.4375", 2 },
{ "31.4750", 3 },
{ "31.6875", 4 },
{ "31.9750", 5 },
{ "TEST 88", 6 }
}
};
Text text_relais {
{ 8, 9 * 16 + 4, 7 * 8, 16 },
"Relais:"
};
ImageOptionsField options_ra {
{ 26, 166, 24, 24 },
{
{ &bulb_ignore_bmp[0], 0 },
{ &bulb_off_bmp[0], 1 },
{ &bulb_on_bmp[0], 2 }
}
};
ImageOptionsField options_rb {
{ 79, 166, 24, 24 },
{
{ &bulb_ignore_bmp[0], 0 },
{ &bulb_off_bmp[0], 1 },
{ &bulb_on_bmp[0], 2 }
}
};
ImageOptionsField options_rc {
{ 133, 166, 24, 24 },
{
{ &bulb_ignore_bmp[0], 0 },
{ &bulb_off_bmp[0], 1 },
{ &bulb_on_bmp[0], 2 }
}
};
ImageOptionsField options_rd {
{ 186, 166, 24, 24 },
{
{ &bulb_ignore_bmp[0], 0 },
{ &bulb_off_bmp[0], 1 },
{ &bulb_on_bmp[0], 2 }
}
};
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"
};
Checkbox checkbox_cligno {
{ 96, 16 * 16},
3,
"J/N"
};
NumberField tempo_cligno {
{ 104, 16 * 16 + 28 },
2,
{ 1, 99 },
1,
' '
};
Text text_cligno {
{ 104 + 16, 16 * 16 + 28, 2 * 8, 16 },
"s."
};
Button button_txtest {
{ 20 * 8, 16 * 16, 64, 32 },
"TEST"
};
MessageHandlerRegistration message_handler_tx_done {
Message::ID::TXDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
this->on_txdone(message.n);
}
};
};
} /* namespace ui */

View File

@ -1,4 +1,4 @@
unsigned char ymdata_bin[] = {
const uint8_t ymdata_bin[] = {
0x00, 0x03, 0x1e, 0x00, 0xb4, 0x01, 0x3e, 0x02, 0x61, 0x05, 0xf7, 0x06,
0x6d, 0x08, 0xc2, 0x09, 0x68, 0x0a, 0x8d, 0x0c, 0x43, 0x0d, 0x9e, 0x0f,
0xef, 0x10, 0x01, 0x11, 0x0f, 0x11, 0x01, 0x64, 0x98, 0x3d, 0x82, 0xc8,

View File

@ -375,19 +375,12 @@ DeclareTargets(PAFS afsk)
#)
#DeclareTargets(PEPR epar)
### Xylos
### Tones
set(MODE_CPPSRC
proc_xylos.cpp
proc_tones.cpp
)
DeclareTargets(PXYL xylos)
### DTMF TX
set(MODE_CPPSRC
proc_dtmf_tx.cpp
)
DeclareTargets(PDTX dtmf_tx)
DeclareTargets(PTON tones)
### RDS

View File

@ -31,6 +31,12 @@
#include <cstddef>
#include <array>
void AudioOutput::configure(
const bool do_proc
) {
do_processing = do_proc;
}
void AudioOutput::configure(
const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config,
@ -69,20 +75,25 @@ void AudioOutput::write(
void AudioOutput::on_block(
const buffer_f32_t& audio
) {
const auto audio_present_now = squelch.execute(audio);
hpf.execute_in_place(audio);
deemph.execute_in_place(audio);
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
const bool audio_present = (audio_present_history != 0);
bool audio_present;
if( !audio_present ) {
for(size_t i=0; i<audio.count; i++) {
audio.p[i] = 0;
}
}
if (do_processing) {
const auto audio_present_now = squelch.execute(audio);
hpf.execute_in_place(audio);
deemph.execute_in_place(audio);
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
audio_present = (audio_present_history != 0);
if( !audio_present ) {
for(size_t i=0; i<audio.count; i++) {
audio.p[i] = 0;
}
}
} else
audio_present = true;
fill_audio_buffer(audio, audio_present);
}

View File

@ -36,6 +36,10 @@
class AudioOutput {
public:
void configure(
const bool do_proc
);
void configure(
const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config = iir_config_passthrough,
@ -64,6 +68,8 @@ private:
AudioStatsCollector audio_stats;
uint64_t audio_present_history = 0;
bool do_processing = true;
void on_block(const buffer_f32_t& audio);
void fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo);

Binary file not shown.

Binary file not shown.

View File

@ -50,7 +50,7 @@ void ADSBTXProcessor::execute(const buffer_c8_t& buffer) {
if (!bit_part) {
if (bit_pos >= 112) {
// Stop
message.n = 200;
message.progress = 200;
shared_memory.application_queue.push(message);
configured = false;
cur_bit = 0;

View File

@ -41,8 +41,8 @@ void AFSKProcessor::execute(const buffer_c8_t& buffer) {
if (sample_count >= afsk_samples_per_bit) {
if (configured) {
cur_byte = shared_memory.tx_data[byte_pos];
ext_byte = shared_memory.tx_data[byte_pos + 1];
cur_byte = shared_memory.bb_data.data[byte_pos];
ext_byte = shared_memory.bb_data.data[byte_pos + 1];
if (!(cur_byte | ext_byte)) {
// End of data
@ -50,16 +50,16 @@ void AFSKProcessor::execute(const buffer_c8_t& buffer) {
// Repeat
bit_pos = 0;
byte_pos = 0;
cur_byte = shared_memory.tx_data[0];
ext_byte = shared_memory.tx_data[1];
message.n = repeat_counter + 1;
cur_byte = shared_memory.bb_data.data[0];
ext_byte = shared_memory.bb_data.data[1];
message.progress = repeat_counter + 1;
shared_memory.application_queue.push(message);
repeat_counter++;
} else {
// Stop
cur_byte = 0;
ext_byte = 0;
message.n = 0;
message.progress = 0;
shared_memory.application_queue.push(message);
configured = false;
}

View File

@ -48,15 +48,15 @@ void DTMFTXProcessor::execute(const buffer_c8_t& buffer) {
tone = true;
timer = tone_length;
tone_code = shared_memory.tx_data[tone_idx];
tone_code = shared_memory.bb_data.data[tone_idx];
if (tone_code == 0xFF) {
txdone_message.n = 0xFF; // End of list
txdone_message.done = true; // End of list
shared_memory.application_queue.push(txdone_message);
configured = false;
tone = false;
} else {
txdone_message.n = tone_idx; // New tone (progress)
txdone_message.progress = tone_idx; // New tone
shared_memory.application_queue.push(txdone_message);
tone_idx++;
}
@ -99,7 +99,7 @@ void DTMFTXProcessor::on_message(const Message* const msg) {
if (message.id == Message::ID::DTMFTXConfig) {
// Translate DTMF message to index in DTMF frequencies table
tone_ptr = &shared_memory.tx_data[0];
tone_ptr = &shared_memory.bb_data.data[0];
for (;;) {
tone_code = *tone_ptr;
if (tone_code == 0xFF)
@ -132,6 +132,8 @@ void DTMFTXProcessor::on_message(const Message* const msg) {
tone_length = message.tone_length * 154; // 153.6
pause_length = message.pause_length * 154; // 153.6
as = 0;
txdone_message.progress = 0;
txdone_message.done = false;
tone = false;
timer = 0;
tone_idx = 0;

View File

@ -87,7 +87,7 @@ void JammerProcessor::execute(const buffer_c8_t& buffer) {
void JammerProcessor::on_message(const Message* const msg) {
jammer_ranges = (JammerRange*)shared_memory.tx_data;
jammer_ranges = (JammerRange*)shared_memory.bb_data.data;
/*const auto message = *reinterpret_cast<const DTMFTXConfigMessage*>(msg);

View File

@ -49,15 +49,15 @@ void OOKProcessor::execute(const buffer_c8_t& buffer) {
if (repeat_counter < repeat) {
// Repeat
bit_pos = 0;
cur_bit = shared_memory.tx_data[0] & 0x80;
message.n = repeat_counter + 1;
shared_memory.application_queue.push(message);
cur_bit = shared_memory.bb_data.data[0] & 0x80;
txdone_message.progress = repeat_counter + 1;
shared_memory.application_queue.push(txdone_message);
repeat_counter++;
} else {
// Stop
cur_bit = 0;
message.n = 0;
shared_memory.application_queue.push(message);
txdone_message.done = true;
shared_memory.application_queue.push(txdone_message);
configured = false;
}
pause_counter = 0;
@ -65,7 +65,7 @@ void OOKProcessor::execute(const buffer_c8_t& buffer) {
pause_counter--;
}
} else {
cur_bit = (shared_memory.tx_data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
bit_pos++;
}
}
@ -108,6 +108,8 @@ void OOKProcessor::on_message(const Message* const p) {
repeat_counter = 0;
bit_pos = 0;
cur_bit = 0;
txdone_message.progress = 0;
txdone_message.done = false;
configured = true;
}
}

View File

@ -52,7 +52,7 @@ private:
uint32_t tone_phase, phase, sphase;
int32_t tone_sample, sig, frq;
TXDoneMessage message;
TXDoneMessage txdone_message;
};
#endif

View File

@ -98,7 +98,7 @@ void RDSProcessor::execute(const buffer_c8_t& buffer) {
void RDSProcessor::on_message(const Message* const msg) {
if (msg->id == Message::ID::RDSConfigure) {
const auto message = *reinterpret_cast<const RDSConfigureMessage*>(msg);
rdsdata = (uint32_t*)shared_memory.tx_data;
rdsdata = (uint32_t*)shared_memory.bb_data.data;
message_length = message.length;
configured = true;
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2015 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 "proc_tones.hpp"
#include "sine_table_int8.hpp"
#include "event_m4.hpp"
#include <cstdint>
// This is called at 1536000/2048 = 750Hz
void TonesProcessor::execute(const buffer_c8_t& buffer) {
if (!configured) return;
ai = 0;
for (size_t i = 0; i < buffer.count; i++) {
// Tone generation at full samplerate
if (silence_count) {
// Just occupy channel with carrier
silence_count--;
if (!silence_count) {
sample_count = 0;
tone_a_phase = 0;
tone_b_phase = 0;
}
tone_sample = 0;
re = 0;
im = 0;
} else {
if (!sample_count) {
digit = shared_memory.bb_data.tones_data.message[digit_pos++];
if (digit_pos >= message_length) {
configured = false;
txdone_message.done = true;
shared_memory.application_queue.push(txdone_message);
} else {
txdone_message.progress = digit_pos; // Inform UI about progress
shared_memory.application_queue.push(txdone_message);
}
if ((digit >= 32) || (tone_deltas[digit] == 0)) {
silence_count = shared_memory.bb_data.tones_data.silence;
} else {
if (!dual_tone) {
tone_a_delta = tone_deltas[digit];
} else {
tone_a_delta = tone_deltas[digit << 1];
tone_b_delta = tone_deltas[(digit << 1) + 1];
}
sample_count = tone_durations[digit];
}
} else {
sample_count--;
}
// Ugly
if (digit >= 32) {
tone_sample = 0;
re = 0;
im = 0;
} else {
if (!dual_tone) {
tone_sample = (sine_table_i8[(tone_a_phase & 0x03FC0000) >> 18]);
tone_a_phase += tone_a_delta;
} else {
tone_sample = sine_table_i8[(tone_a_phase & 0x03FC0000) >> 18] >> 1;
tone_sample += sine_table_i8[(tone_b_phase & 0x03FC0000) >> 18] >> 1;
tone_a_phase += tone_a_delta;
tone_b_phase += tone_b_delta;
}
// FM
delta = tone_sample * fm_delta;
phase += delta;
sphase = phase + (64 << 18);
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
}
}
// Headphone output sample generation: 1536000/24000 = 64
if (audio_out) {
if (!as) {
as = 64; // 63 ?
audio_buffer.p[ai++] = tone_sample * 128;
} else {
as--;
}
}
buffer.p[i] = {(int8_t)re, (int8_t)im};
}
if (audio_out) audio_output.write(audio_buffer);
}
void TonesProcessor::on_message(const Message* const p) {
const auto message = *reinterpret_cast<const TonesConfigureMessage*>(p);
if (message.id == Message::ID::TonesConfigure) {
silence_count = message.pre_silence; // In samples
for (uint8_t c = 0; c < 32; c++) {
tone_deltas[c] = shared_memory.bb_data.tones_data.tone_defs[c].delta;
tone_durations[c] = shared_memory.bb_data.tones_data.tone_defs[c].duration;
}
message_length = message.tone_count;
fm_delta = message.fm_delta;
audio_out = message.audio_out;
dual_tone = message.dual_tone;
if (audio_out) audio_output.configure(false);
txdone_message.done = false;
txdone_message.progress = 0;
digit_pos = 0;
tone_a_phase = 0;
tone_b_phase = 0;
as = 0;
configured = true;
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<TonesProcessor>() };
event_dispatcher.run();
return 0;
}

View File

@ -20,18 +20,15 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef __PROC_XYLOS_H__
#define __PROC_XYLOS_H__
#ifndef __PROC_TONES_H__
#define __PROC_TONES_H__
#include "portapack_shared_memory.hpp"
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "audio_output.hpp"
//#include "audio_output.hpp"
#define CCIR_PHASEINC (436.91/2) // (65536*1024)/1536000*10
#define CCIR_SILENCE (122880)-1 // 400ms
class XylosProcessor : public BasebandProcessor {
class TonesProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
@ -42,37 +39,31 @@ private:
BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
const uint32_t ccir_phases[16] = {
(uint32_t)(1981*CCIR_PHASEINC),
(uint32_t)(1124*CCIR_PHASEINC),
(uint32_t)(1197*CCIR_PHASEINC),
(uint32_t)(1275*CCIR_PHASEINC),
(uint32_t)(1358*CCIR_PHASEINC),
(uint32_t)(1446*CCIR_PHASEINC),
(uint32_t)(1540*CCIR_PHASEINC),
(uint32_t)(1640*CCIR_PHASEINC),
(uint32_t)(1747*CCIR_PHASEINC),
(uint32_t)(1860*CCIR_PHASEINC),
(uint32_t)(2400*CCIR_PHASEINC),
(uint32_t)(930*CCIR_PHASEINC),
(uint32_t)(2247*CCIR_PHASEINC),
(uint32_t)(991*CCIR_PHASEINC),
(uint32_t)(2110*CCIR_PHASEINC),
(uint32_t)(1055*CCIR_PHASEINC)
};
std::array<int16_t, 32> audio; // 2048/64
const buffer_s16_t audio_buffer {
(int16_t*)audio.data(),
sizeof(audio) / sizeof(int16_t)
};
uint32_t samples_per_tone;
int8_t re, im;
uint8_t s, as = 0, ai;
uint8_t byte_pos = 0;
uint8_t digit = 0;
uint32_t sample_count = 0;
uint32_t tone_phase, phase, sphase;
int32_t tone_sample, delta;
bool silence = true;
TXDoneMessage message;
uint32_t tone_deltas[32];
uint32_t tone_durations[32];
//AudioOutput audio_output;
bool audio_out;
bool dual_tone;
uint32_t fm_delta;
uint32_t tone_a_phase, tone_b_phase;
uint32_t tone_a_delta, tone_b_delta;
uint8_t digit_pos;
uint8_t digit;
uint32_t silence_count, sample_count;
uint32_t message_length;
uint32_t phase, sphase;
int32_t tone_sample, delta;
int8_t re, im;
uint8_t as, ai;
TXDoneMessage txdone_message;
AudioOutput audio_output;
};
#endif

View File

@ -1,122 +0,0 @@
/*
* Copyright (C) 2015 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 "proc_xylos.hpp"
#include "portapack_shared_memory.hpp"
#include "sine_table_int8.hpp"
#include "event_m4.hpp"
#include <cstdint>
void XylosProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 1536000/2048 = 750Hz
if (!configured) return;
for (size_t i = 0; i<buffer.count; i++) {
// Tone generation at 1536000/5 = 307.2kHz
if (s >= (5 - 1)) {
s = 0;
if (silence) {
// Just occupy channel with carrier
if (sample_count >= CCIR_SILENCE) {
silence = false;
sample_count = samples_per_tone;
} else {
sample_count++;
}
} else {
if (sample_count >= samples_per_tone) {
digit = shared_memory.tx_data[byte_pos++];
if ((digit == 0xFF) || (byte_pos >= 21)) {
configured = false;
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)
shared_memory.application_queue.push(message);
}
sample_count = 0;
} else {
sample_count++;
}
tone_phase += ccir_phases[digit];
}
} else {
s++;
}
if (silence) {
re = 0;
im = 0;
} else {
tone_sample = (sine_table_i8[(tone_phase & 0x03FC0000) >> 18]);
// Audio preview sample generation: 1536000/48000 = 32
/*if (as >= 31) {
as = 0;
audio[ai++] = sample * 128;
} else {
as++;
}*/
// FM
// 1<<18 = 262144
// m = (262144 * BW) / 1536000 / 2
delta = tone_sample * 853; // 10kHz BW
phase += delta;
sphase = phase + (64 << 18);
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
}
buffer.p[i] = {(int8_t)re, (int8_t)im};
}
//audio_output.write(audio_buffer);
}
void XylosProcessor::on_message(const Message* const p) {
const auto message = *reinterpret_cast<const CCIRConfigureMessage*>(p);
if (message.id == Message::ID::CCIRConfigure) {
byte_pos = 0;
digit = 0;
samples_per_tone = message.samples_per_tone;
sample_count = samples_per_tone;
as = 0;
silence = true;
configured = true;
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<XylosProcessor>() };
event_dispatcher.run();
return 0;
}

Binary file not shown.

View File

@ -23,7 +23,7 @@ __process_stack_size__ = 0x1000; /* main() stack */
MEMORY
{
flash : org = 0x00000000, len = 256k /* SPIFI flash @ 0x140????? */
flash : org = 0x00000000, len = 512k /* SPIFI flash @ 0x140????? */
ram : org = 0x20000000, len = 64k /* AHB SRAM @ 0x20000000 */
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -68,7 +69,7 @@ public:
TXDone = 20,
Retune = 21,
CCIRConfigure = 22,
TonesConfigure = 22,
AFSKConfigure = 23,
PWMRSSIConfigure = 24,
OOKConfigure = 25,
@ -498,7 +499,8 @@ public:
{
}
int n = 0;
uint32_t progress = 0;
bool done = false;
};
@ -521,19 +523,28 @@ public:
const int32_t avg;
};
class CCIRConfigureMessage : public Message {
class TonesConfigureMessage : public Message {
public:
constexpr CCIRConfigureMessage(
const uint32_t samples_per_tone,
const uint16_t tone_count
) : Message { ID::CCIRConfigure },
samples_per_tone(samples_per_tone),
tone_count(tone_count)
constexpr TonesConfigureMessage(
const uint32_t fm_delta,
const uint32_t pre_silence,
const uint16_t tone_count,
const bool dual_tone,
const bool audio_out
) : Message { ID::TonesConfigure },
fm_delta(fm_delta),
pre_silence(pre_silence),
tone_count(tone_count),
dual_tone(dual_tone),
audio_out(audio_out)
{
}
const uint32_t samples_per_tone;
const uint32_t fm_delta;
const uint32_t pre_silence;
const uint16_t tone_count;
const bool dual_tone;
const bool audio_out;
};
class RDSConfigureMessage : public Message {

View File

@ -184,6 +184,14 @@ void set_playdead_sequence(const uint32_t new_value) {
data->playdead_sequence = new_value;
}
bool stealth_mode() {
return ((data->ui_config >> 3) & 1) ? true : false;
}
void set_stealth_mode(const bool new_value) {
data->ui_config = (data->ui_config & ~0b1000) | ((new_value & 1) << 3);
}
uint32_t ui_config() {
uint8_t bloff_value;
@ -210,7 +218,7 @@ uint16_t ui_config_bloff() {
}
void set_config_textentry(uint8_t new_value) {
data->ui_config = (data->ui_config & ~0b1100) | ((new_value & 1) << 2);
data->ui_config = (data->ui_config & ~0b100) | ((new_value & 1) << 2);
}
uint8_t ui_config_textentry() {

View File

@ -65,6 +65,9 @@ void set_playing_dead(const uint32_t new_value);
uint32_t playdead_sequence();
void set_playdead_sequence(const uint32_t new_value);
bool stealth_mode();
void set_stealth_mode(const bool new_value);
uint32_t ui_config();
void set_ui_config(const uint32_t new_value);

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -25,6 +26,7 @@
#include <cstdint>
#include <cstddef>
#include "portapack_shared_memory.hpp"
#include "message_queue.hpp"
struct JammerRange {
@ -34,6 +36,17 @@ struct JammerRange {
uint32_t duration;
};
struct ToneDef {
uint32_t delta;
uint32_t duration;
};
struct ToneData {
ToneDef tone_defs[32];
uint32_t silence;
uint8_t message[128];
};
/* NOTE: These structures must be located in the same location in both M4 and M0 binaries */
struct SharedMemory {
static constexpr size_t application_queue_k = 11;
@ -47,8 +60,11 @@ struct SharedMemory {
char m4_panic_msg[32] { 0 };
// struct tx_data union for 9x JammerRange ?
uint8_t tx_data[512] { 0 };
union {
ToneData tones_data;
JammerRange jammer_ranges[9];
uint8_t data[512];
} bb_data;
};
extern SharedMemory& shared_memory;

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,10 +78,9 @@ constexpr image_tag_t image_tag_jammer { 'P', 'J', 'A', 'M' };
constexpr image_tag_t image_tag_audio_tx { 'P', 'A', 'T', 'X' };
constexpr image_tag_t image_tag_afsk { 'P', 'A', 'F', 'S' };
constexpr image_tag_t image_tag_epar { 'P', 'E', 'P', 'R' };
constexpr image_tag_t image_tag_xylos { 'P', 'X', 'Y', 'L' };
constexpr image_tag_t image_tag_tones { 'P', 'T', 'O', 'N' };
constexpr image_tag_t image_tag_rds { 'P', 'R', 'D', 'S' };
constexpr image_tag_t image_tag_ook { 'P', 'O', 'O', 'K' };
constexpr image_tag_t image_tag_dtmf_tx { 'P', 'D', 'T', 'X' };
constexpr image_tag_t image_tag_adsb_tx { 'P', 'A', 'D', 'S' };
constexpr image_tag_t image_tag_hackrf { 'H', 'R', 'F', '1' };

View File

@ -250,6 +250,12 @@ void View::remove_child(Widget* const widget) {
}
}
void View::remove_children(const std::vector<Widget*>& children) {
for(auto child : children) {
remove_child(child);
}
}
const std::vector<Widget*>& View::children() const {
return children_;
}
@ -564,13 +570,17 @@ void Checkbox::set_text(const std::string value) {
set_dirty();
}
/*std::string Checkbox::text() const {
return text_;
}*/
void Checkbox::set_value(const bool value) {
bool Checkbox::set_value(const bool value) {
value_ = value;
if( on_select ) {
on_select(*this);
return true;
}
set_dirty();
return false;
}
bool Checkbox::value() const {
@ -616,15 +626,8 @@ void Checkbox::paint(Painter& painter) {
}
bool Checkbox::on_key(const KeyEvent key) {
if( key == KeyEvent::Select ) {
value_ = not value_;
set_dirty();
if( on_select ) {
on_select(*this);
return true;
}
}
if( key == KeyEvent::Select )
return set_value(not value_);
return false;
}
@ -714,8 +717,7 @@ bool Button::on_key(const KeyEvent key) {
}
} else {
if( on_dir ) {
on_dir(*this, key);
return false;
return on_dir(*this, key);
}
}

View File

@ -29,6 +29,7 @@
#include "ui_focus.hpp"
#include "radio.hpp"
#include "portapack.hpp"
#include "utility.hpp"
#include <memory>
@ -161,6 +162,7 @@ public:
void add_child(Widget* const widget);
void add_children(const std::vector<Widget*>& children);
void remove_child(Widget* const widget);
void remove_children(const std::vector<Widget*>& children);
const std::vector<Widget*>& children() const override;
virtual std::string title() const;
@ -264,7 +266,7 @@ public:
void set_text(const std::string value);
// std::string text() const;
void set_value(const bool value);
bool set_value(const bool value);
bool value() const;
void paint(Painter& painter) override;
@ -281,7 +283,7 @@ private:
class Button : public Widget {
public:
std::function<void(Button&)> on_select;
std::function<void(Button&,KeyEvent)> on_dir;
std::function<bool(Button&,KeyEvent)> on_dir;
std::function<void(Button&)> on_highlight;
Button(Rect parent_rect, std::string text);
@ -354,6 +356,11 @@ public:
ImageOptionsField(Rect parent_rect, options_t options);
ImageOptionsField(
) : ImageOptionsField { { }, { } }
{
}
void set_options(options_t new_options);
size_t selected_index() const;

Binary file not shown.