1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-19 22:35:12 +03:00

[X10 Express] Spectrum analyser, first version

This commit is contained in:
Bertrand Songis 2019-08-19 16:21:44 +02:00
parent 01c88fbba8
commit ffb731dc30
No known key found for this signature in database
GPG key ID: F189F79290FEC50F
11 changed files with 281 additions and 140 deletions

View file

@ -419,7 +419,6 @@ if(NOT MSVC)
set(OPT s)
if(ARCH STREQUAL ARM)
enable_language(ASM)
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)

View file

@ -0,0 +1,172 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "opentx.h"
extern uint8_t g_moduleIdx;
enum SpectrumFields {
SPECTRUM_FREQUENCY,
SPECTRUM_SPAN,
SPECTRUM_FIELDS_MAX
};
bool menuRadioSpectrumAnalyser(event_t event)
{
SUBMENU(STR_MENU_SPECTRUM_ANALYSER, ICON_RADIO, 1, {1});
if (menuEvent) {
lcdDrawCenteredText(LCD_H/2, STR_STOPPING);
lcdRefresh();
moduleState[g_moduleIdx].readModuleInformation(&reusableBuffer.moduleSetup.pxx2.moduleInformation, PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID);
/* wait 1s to resume normal operation before leaving */
watchdogSuspend(1000);
RTOS_WAIT_MS(1000);
return false;
}
if (moduleState[g_moduleIdx].mode != MODULE_MODE_SPECTRUM_ANALYSER) {
if (TELEMETRY_STREAMING()) {
lcdDrawCenteredText(LCD_H/2, STR_TURN_OFF_RECEIVER);
if (event == EVT_KEY_FIRST(KEY_EXIT)) {
killEvents(event);
popMenu();
}
return false;
}
memclear(&reusableBuffer.spectrumAnalyser, sizeof(reusableBuffer.spectrumAnalyser));
if (isModuleR9MAccess(g_moduleIdx)) {
reusableBuffer.spectrumAnalyser.spanDefault = 20;
reusableBuffer.spectrumAnalyser.spanMax = 40;
reusableBuffer.spectrumAnalyser.freqDefault = 890;
reusableBuffer.spectrumAnalyser.freqMin = 850;
reusableBuffer.spectrumAnalyser.freqMax = 930;
}
else {
reusableBuffer.spectrumAnalyser.spanDefault = 40; // 40MHz
reusableBuffer.spectrumAnalyser.spanMax = 80;
reusableBuffer.spectrumAnalyser.freqDefault = 2440; // 2440MHz
reusableBuffer.spectrumAnalyser.freqMin = 2400;
reusableBuffer.spectrumAnalyser.freqMax = 2485;
}
reusableBuffer.spectrumAnalyser.span = reusableBuffer.spectrumAnalyser.spanDefault * 1000000;
reusableBuffer.spectrumAnalyser.freq = reusableBuffer.spectrumAnalyser.freqDefault * 1000000;
reusableBuffer.spectrumAnalyser.step = reusableBuffer.spectrumAnalyser.span / LCD_W;
reusableBuffer.spectrumAnalyser.dirty = true;
moduleState[g_moduleIdx].mode = MODULE_MODE_SPECTRUM_ANALYSER;
}
for (uint8_t i=0; i<SPECTRUM_FIELDS_MAX; i++) {
LcdFlags attr = (menuHorizontalPosition == i ? (s_editMode>0 ? INVERS|BLINK : INVERS) : 0);
switch (i) {
case SPECTRUM_FREQUENCY: {
uint16_t frequency = reusableBuffer.spectrumAnalyser.freq / 1000000;
lcdDrawText(MENUS_MARGIN_LEFT, MENU_FOOTER_TOP, "F:", TEXT_INVERTED_COLOR);
lcdDrawNumber(lcdNextPos + 2, MENU_FOOTER_TOP, frequency, attr | TEXT_INVERTED_COLOR);
lcdDrawText(lcdNextPos + 2, MENU_FOOTER_TOP, "MHz", TEXT_INVERTED_COLOR);
if (attr) {
reusableBuffer.spectrumAnalyser.freq = uint32_t(checkIncDec(event, frequency, reusableBuffer.spectrumAnalyser.freqMin, reusableBuffer.spectrumAnalyser.freqMax, 0)) * 1000000;
if (checkIncDec_Ret) {
reusableBuffer.spectrumAnalyser.dirty = true;
}
}
break;
}
case SPECTRUM_SPAN:
uint8_t span = reusableBuffer.spectrumAnalyser.span / 1000000;
lcdDrawText(MENUS_MARGIN_LEFT + 100, MENU_FOOTER_TOP, "S:", TEXT_INVERTED_COLOR);
lcdDrawNumber(lcdNextPos + 2, MENU_FOOTER_TOP, reusableBuffer.spectrumAnalyser.span/1000000, attr | TEXT_INVERTED_COLOR);
lcdDrawText(lcdNextPos + 2, MENU_FOOTER_TOP, "MHz", TEXT_INVERTED_COLOR);
if (attr) {
reusableBuffer.spectrumAnalyser.span = checkIncDec(event, span, 1, reusableBuffer.spectrumAnalyser.spanMax, 0) * 1000000;
if (checkIncDec_Ret) {
reusableBuffer.spectrumAnalyser.step = reusableBuffer.spectrumAnalyser.span / LCD_W;
reusableBuffer.spectrumAnalyser.dirty = true;
}
}
break;
}
}
for (uint32_t frequency = ((reusableBuffer.spectrumAnalyser.freq - reusableBuffer.spectrumAnalyser.span / 2) / 10000000) * 10000000; frequency < reusableBuffer.spectrumAnalyser.freq + reusableBuffer.spectrumAnalyser.span / 2; frequency += 10000000) {
int32_t offset = frequency - (reusableBuffer.spectrumAnalyser.freq - reusableBuffer.spectrumAnalyser.span / 2);
uint32_t x = offset / reusableBuffer.spectrumAnalyser.step;
if (x > 0 && x < LCD_W - 1)
lcdDrawVerticalLine(x, MENU_HEADER_HEIGHT, LCD_H - MENU_HEADER_HEIGHT - MENU_FOOTER_HEIGHT, STASHED, CURVE_AXIS_COLOR);
}
for (coord_t y = MENU_HEADER_HEIGHT + (MENU_FOOTER_TOP - MENU_HEADER_HEIGHT) / 8; y < MENU_FOOTER_TOP; y += (MENU_FOOTER_TOP - MENU_HEADER_HEIGHT) / 8) {
lcdDrawHorizontalLine(0, y, LCD_W, STASHED, CURVE_AXIS_COLOR);
}
coord_t peak_y = LCD_H;
coord_t peak_x = 0;
coord_t prev_yv = (coord_t)-1;
for (coord_t xv=0; xv<LCD_W; xv++) {
coord_t yv = MENU_FOOTER_TOP - limit<int>(0, reusableBuffer.spectrumAnalyser.bars[xv] << 1, LCD_H - MENU_HEADER_HEIGHT - MENU_FOOTER_HEIGHT);
if (prev_yv != (coord_t)-1) {
if (yv < peak_y) {
peak_x = xv;
peak_y = yv;
}
if (prev_yv < yv) {
for (int y=prev_yv; y<=yv; y+=1) {
lcdDrawPoint(xv, y, TEXT_COLOR);
}
}
else {
for (int y=yv; y<=prev_yv; y+=1) {
lcdDrawPoint(xv, y, TEXT_COLOR);
}
}
}
prev_yv = yv;
}
prev_yv = (coord_t)-1;
for (coord_t xv=0; xv<LCD_W; xv++) {
coord_t yv = MENU_FOOTER_TOP - limit<int>(0, reusableBuffer.spectrumAnalyser.max[xv] << 1, LCD_H - MENU_HEADER_HEIGHT - MENU_FOOTER_HEIGHT);
if (prev_yv != (coord_t)-1) {
if (prev_yv < yv) {
for (int y=prev_yv; y<=yv; y+=1) {
lcdDrawPoint(xv, y, TEXT_INVERTED_BGCOLOR);
}
}
else {
for (int y=yv; y<=prev_yv; y+=1) {
lcdDrawPoint(xv, y, TEXT_INVERTED_BGCOLOR);
}
}
}
prev_yv = yv;
}
coord_t y = max<coord_t>(MENU_HEADER_HEIGHT + 1, peak_y - FH);
lcdDrawNumber(limit<coord_t>(20, peak_x, LCD_W - 20), y, ((reusableBuffer.spectrumAnalyser.freq - reusableBuffer.spectrumAnalyser.span / 2) + peak_x * (reusableBuffer.spectrumAnalyser.span / LCD_W)) / 1000000, TINSIZE | CENTERED);
lcdDrawText(lcdNextPos, y, "M", TINSIZE);
return true;
}

View file

@ -27,9 +27,9 @@ bool addRadioTool(uint8_t index, const char * label)
{
int8_t sub = menuVerticalPosition - HEADER_LINE;
LcdFlags attr = (sub == index ? INVERS : 0);
coord_t y = MENU_HEADER_HEIGHT + 1 + index * FH;
lcdDrawNumber(3, y, index + 1, LEADING0|LEFT, 2);
// BSS lcdDrawText(3*FW, y, label, (sub == index ? INVERS : 0));
coord_t y = MENU_CONTENT_TOP + index * FH;
lcdDrawNumber(MENUS_MARGIN_LEFT, y, index + 1, LEADING0|LEFT, 2);
lcdDrawText(30, y, label, (sub == index ? INVERS : 0));
if (attr && s_editMode > 0) {
s_editMode = 0;
killAllEvents();
@ -46,52 +46,13 @@ void addRadioModuleTool(uint8_t index, const char * label, bool (* tool)(event_t
}
}
#define TOOL_NAME_MAXLEN 16
#if defined(LUA)
bool readToolName(const char * filename, char * name)
{
FIL file;
char buffer[1024];
UINT count;
if (f_open(&file, filename, FA_READ) != FR_OK) {
return "Error opening file";
}
if (f_read(&file, &buffer, sizeof(buffer), &count) != FR_OK) {
f_close(&file);
return false;
}
const char * tns = "TNS|";
auto * start = std::search(buffer, buffer + sizeof(buffer), tns, tns + 4);
if (start >= buffer + sizeof(buffer))
return false;
start += 4;
const char * tne = "|TNE";
auto * end = std::search(buffer, buffer + sizeof(buffer), tne, tne + 4);
if (end >= buffer + sizeof(buffer) || end <= start)
return false;
uint8_t len = end - start;
if (len > TOOL_NAME_MAXLEN)
return false;
strncpy(name, start, len);
memclear(name + len, TOOL_NAME_MAXLEN + 1 - len);
return true;
}
void addRadioScriptTool(uint8_t index, const char * path)
{
char toolName[TOOL_NAME_MAXLEN + 1];
const char * label;
char * ext = (char *)getFileExtension(path);
if (readToolName(path, toolName)) {
if (readToolName(toolName, path)) {
label = toolName;
}
else {
@ -105,12 +66,6 @@ void addRadioScriptTool(uint8_t index, const char * path)
luaExec(path);
}
}
bool isRadioScriptTool(const char * filename)
{
const char * ext = getFileExtension(filename);
return ext && !strcasecmp(ext, SCRIPT_EXT);
}
#endif
bool menuRadioTools(event_t event)
@ -130,18 +85,18 @@ bool menuRadioTools(event_t event)
uint8_t index = 0;
#if 0 // TODO BSS defined(PXX2)
#if defined(PXX2)
if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER))
addRadioModuleTool(index++, STR_SPECTRUM_ANALYSER_INT, menuRadioSpectrumAnalyser, INTERNAL_MODULE);
if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER))
addRadioModuleTool(index++, STR_POWER_METER_INT, menuRadioPowerMeter, INTERNAL_MODULE);
// if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER))
// addRadioModuleTool(index++, STR_POWER_METER_INT, menuRadioPowerMeter, INTERNAL_MODULE);
if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER))
addRadioModuleTool(index++, STR_SPECTRUM_ANALYSER_EXT, menuRadioSpectrumAnalyser, EXTERNAL_MODULE);
if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER))
addRadioModuleTool(index++, STR_POWER_METER_EXT, menuRadioPowerMeter, EXTERNAL_MODULE);
// if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER))
// addRadioModuleTool(index++, STR_POWER_METER_EXT, menuRadioPowerMeter, EXTERNAL_MODULE);
#endif
#if defined(LUA)

View file

@ -32,15 +32,6 @@ void menuRadioSpectrumAnalyser(event_t event)
{
SUBMENU(STR_MENU_SPECTRUM_ANALYSER, 1, {1});
if (TELEMETRY_STREAMING()) {
lcdDrawCenteredText(LCD_H/2, STR_TURN_OFF_RECEIVER);
if (event == EVT_KEY_FIRST(KEY_EXIT)) {
killEvents(event);
popMenu();
}
return;
}
if (menuEvent) {
lcdDrawCenteredText(LCD_H/2, STR_STOPPING);
lcdRefresh();
@ -51,6 +42,18 @@ void menuRadioSpectrumAnalyser(event_t event)
return;
}
if (moduleState[g_moduleIdx].mode != MODULE_MODE_SPECTRUM_ANALYSER) {
if (TELEMETRY_STREAMING()) {
lcdDrawCenteredText(LCD_H/2, STR_TURN_OFF_RECEIVER);
if (event == EVT_KEY_FIRST(KEY_EXIT)) {
killEvents(event);
popMenu();
}
return;
}
memclear(reusableBuffer.spectrumAnalyser.bars, sizeof(reusableBuffer.spectrumAnalyser.bars));
if (isModuleR9MAccess(g_moduleIdx)) {
reusableBuffer.spectrumAnalyser.spanDefault = 20;
reusableBuffer.spectrumAnalyser.spanMax = 40;
@ -66,8 +69,6 @@ void menuRadioSpectrumAnalyser(event_t event)
reusableBuffer.spectrumAnalyser.freqMax = 2485;
}
if (moduleState[g_moduleIdx].mode != MODULE_MODE_SPECTRUM_ANALYSER) {
memclear(reusableBuffer.spectrumAnalyser.bars, sizeof(reusableBuffer.spectrumAnalyser.bars));
reusableBuffer.spectrumAnalyser.span = reusableBuffer.spectrumAnalyser.spanDefault * 1000000;
reusableBuffer.spectrumAnalyser.freq = reusableBuffer.spectrumAnalyser.freqDefault * 1000000;
reusableBuffer.spectrumAnalyser.step = reusableBuffer.spectrumAnalyser.span / LCD_W;

View file

@ -49,48 +49,11 @@ void addRadioModuleTool(uint8_t index, const char * label, void (* tool)(event_t
#define TOOL_NAME_MAXLEN 16
#if defined(LUA)
bool readToolName(const char * filename, char * name)
{
FIL file;
char buffer[1024];
UINT count;
if (f_open(&file, filename, FA_READ) != FR_OK) {
return "Error opening file";
}
if (f_read(&file, &buffer, sizeof(buffer), &count) != FR_OK) {
f_close(&file);
return false;
}
const char * tns = "TNS|";
auto * start = std::search(buffer, buffer + sizeof(buffer), tns, tns + 4);
if (start >= buffer + sizeof(buffer))
return false;
start += 4;
const char * tne = "|TNE";
auto * end = std::search(buffer, buffer + sizeof(buffer), tne, tne + 4);
if (end >= buffer + sizeof(buffer) || end <= start)
return false;
uint8_t len = end - start;
if (len > TOOL_NAME_MAXLEN)
return false;
strncpy(name, start, len);
memclear(name + len, TOOL_NAME_MAXLEN + 1 - len);
return true;
}
void addRadioScriptTool(uint8_t index, const char * path)
{
char toolName[TOOL_NAME_MAXLEN + 1];
if (!readToolName(path, toolName)) {
if (!readToolName(toolName, path)) {
strAppendFilename(toolName, getBasename(path), TOOL_NAME_MAXLEN);
}
@ -102,12 +65,6 @@ void addRadioScriptTool(uint8_t index, const char * path)
luaExec(path);
}
}
bool isRadioScriptTool(const char * filename)
{
const char * ext = getFileExtension(filename);
return ext && !strcasecmp(ext, SCRIPT_EXT);
}
#endif
void menuRadioTools(event_t event)

View file

@ -22,6 +22,7 @@
#include <ctype.h>
#include <stdio.h>
#include <algorithm>
#include "opentx.h"
#include "bin_allocator.h"
#include "lua_api.h"
@ -1073,7 +1074,6 @@ uint32_t luaGetMemUsed(lua_State * L)
return L ? (lua_gc(L, LUA_GCCOUNT, 0) << 10) + lua_gc(L, LUA_GCCOUNTB, 0) : 0;
}
void luaInit()
{
TRACE("luaInit");
@ -1116,3 +1116,46 @@ void luaInit()
}
}
}
bool readToolName(char * toolName, const char * filename)
{
FIL file;
char buffer[1024];
UINT count;
if (f_open(&file, filename, FA_READ) != FR_OK) {
return "Error opening file";
}
if (f_read(&file, &buffer, sizeof(buffer), &count) != FR_OK) {
f_close(&file);
return false;
}
const char * tns = "TNS|";
auto * start = std::search(buffer, buffer + sizeof(buffer), tns, tns + 4);
if (start >= buffer + sizeof(buffer))
return false;
start += 4;
const char * tne = "|TNE";
auto * end = std::search(buffer, buffer + sizeof(buffer), tne, tne + 4);
if (end >= buffer + sizeof(buffer) || end <= start)
return false;
uint8_t len = end - start;
if (len > TOOL_NAME_MAXLEN)
return false;
strncpy(toolName, start, len);
memclear(toolName + len, TOOL_NAME_MAXLEN + 1 - len);
return true;
}
bool isRadioScriptTool(const char * filename)
{
const char * ext = getFileExtension(filename);
return ext && !strcasecmp(ext, SCRIPT_EXT);
}

View file

@ -171,6 +171,10 @@ void registerBitmapClass(lua_State * L);
void luaSetInstructionsLimit(lua_State* L, int count);
int luaLoadScriptFileToState(lua_State * L, const char * filename, const char * mode);
#define TOOL_NAME_MAXLEN 16
bool readToolName(char * toolName, const char * filename);
bool isRadioScriptTool(const char * filename);
struct LuaMemTracer {
const char * script;
int lineno;

View file

@ -1187,6 +1187,9 @@ union ReusableBuffer
struct {
uint8_t bars[LCD_W];
#if defined(COLORLCD)
uint8_t max[LCD_W];
#endif
uint32_t freq;
uint32_t span;
uint32_t step;

View file

@ -301,6 +301,9 @@ void Pxx2Pulses::setupSpectrumAnalyser(uint8_t module)
{
if (reusableBuffer.spectrumAnalyser.dirty) {
reusableBuffer.spectrumAnalyser.dirty = false;
#if defined(PCBHORUS)
memclear(&reusableBuffer.spectrumAnalyser.max, sizeof(reusableBuffer.spectrumAnalyser.max));
#endif
addFrameType(PXX2_TYPE_C_POWER_METER, PXX2_TYPE_ID_SPECTRUM);
Pxx2Transport::addByte(0x00);
Pxx2Transport::addWord(reusableBuffer.spectrumAnalyser.freq);

View file

@ -88,6 +88,15 @@ if(NOT UNEXPECTED_SHUTDOWN)
add_definitions(-DNO_UNEXPECTED_SHUTDOWN)
endif()
if(INTERNAL_MODULE_PXX1)
add_definitions(-DINTERNAL_MODULE_PXX1)
endif()
if(INTERNAL_MODULE_PXX2)
set(PXX2 ON)
add_definitions(-DINTERNAL_MODULE_PXX2)
endif()
include_directories(${RADIO_SRC_DIRECTORY}/fonts/480x272 gui/${GUI_DIR} gui/${GUI_DIR}/layouts)
file(GLOB THEMES_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/480x272 ${RADIO_SRC_DIRECTORY}/gui/480x272/themes/*.cpp)
@ -123,13 +132,13 @@ if(PXX2 OR LUA)
set(GUI_SRC ${GUI_SRC} radio_tools.cpp)
endif()
#if(PXX2)
# set(GUI_SRC
# ${GUI_SRC}
# ../common/stdlcd/radio_spectrum_analyser.cpp
# ../common/stdlcd/radio_power_meter.cpp
# )
#endif()
if(PXX2)
set(GUI_SRC
${GUI_SRC}
radio_spectrum_analyser.cpp
# radio_power_meter.cpp
)
endif()
if(DISK_CACHE)
set(SRC ${SRC} disk_cache.cpp)
@ -191,12 +200,3 @@ if(PYTHONINTERP_FOUND)
DEPENDS ${RADIO_DIRECTORY}/src/datastructs.h ${RADIO_DIRECTORY}/util/generate_datacopy.py
)
endif()
if(INTERNAL_MODULE_PXX1)
add_definitions(-DINTERNAL_MODULE_PXX1)
endif()
if(INTERNAL_MODULE_PXX2)
set(PXX2 ON)
add_definitions(-DINTERNAL_MODULE_PXX2)
endif()

View file

@ -238,6 +238,10 @@ void processSpectrumAnalyserFrame(uint8_t module, uint8_t * frame)
uint32_t x = offset / reusableBuffer.spectrumAnalyser.step;
if (x < LCD_W) {
reusableBuffer.spectrumAnalyser.bars[x] = 0x80 + power;
#if defined(COLORLCD)
if (reusableBuffer.spectrumAnalyser.bars[x] > reusableBuffer.spectrumAnalyser.max[x])
reusableBuffer.spectrumAnalyser.max[x] = reusableBuffer.spectrumAnalyser.bars[x];
#endif
}
}