1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-15 12:25:15 +03:00

Add HD OSD support (#3071)

* Add HD OSD mode

* Update src/js/tabs/osd.js

Co-authored-by: Jan Post <Rm2k-Freak@web.de>

Co-authored-by: Jan Post <Rm2k-Freak@web.de>
This commit is contained in:
Steve Evans 2022-11-20 17:42:52 +00:00 committed by GitHub
parent f20290451a
commit 266d5f445d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 271 additions and 70 deletions

View file

@ -4790,6 +4790,10 @@
"message": "NTSC",
"description": "Option for the video format in the OSD"
},
"osdSetupVideoFormatOptionHd": {
"message": "HD",
"description": "Option for the video format in the OSD"
},
"osdSetupUnitsTitle": {
"message": "Units"
},
@ -5020,6 +5024,83 @@
"message": "Aux value",
"description": "One of the elements of the OSD"
},
"osdTextElementSysGoggleVoltage": {
"message": "Goggle voltage",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysGoggleVoltage": {
"message": "Goggle voltage rendered by the goggles"
},
"osdTextElementSysVtxVoltage": {
"message": "VTX voltage",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysVtxVoltage": {
"message": "VTX voltage rendered by the goggles"
},
"osdTextElementSysBitrate": {
"message": "VTX bitrate",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysBitrate": {
"message": "Video bitrate rendered by the goggles"
},
"osdTextElementSysDelay": {
"message": "VTX delay",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysDelay": {
"message": "Video delay rendered by the goggles"
},
"osdTextElementSysDistance": {
"message": "VTX distance",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysDistance": {
"message": "Video transmission distance rendered by the goggles"
},
"osdTextElementSysLQ": {
"message": "Goggle link quality",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysLQ": {
"message": "Video link quality rendered by the goggles"
},
"osdTextElementSysGoggleDVR": {
"message": "Goggle DVR status",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysGoggleDVR": {
"message": "Goggle DVR status rendered by the goggles"
},
"osdTextElementSysVtxDVR": {
"message": "VTX DVR status",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysVtxDVR": {
"message": "VTX DVR status rendered by the goggles"
},
"osdTextElementSysWarnings": {
"message": "Goggle system warnings",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysWarnings": {
"message": "Video system warnings rendered by the goggles"
},
"osdTextElementSysVtxTemp": {
"message": "VTX temperature",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysVtxTemp": {
"message": "VTX temperature rendered by the goggles"
},
"osdTextElementSysFanSpeed": {
"message": "Goggle fan speed",
"description": "One of the system elements of the OSD"
},
"osdDescElementSysFanSpeed": {
"message": "Goggle fan speed rendered by the goggles"
},
"osdTextElementCraftName": {
"message": "Craft name",
"description": "One of the elements of the OSD"

View file

@ -534,10 +534,6 @@ progress[value] {
.preview {
background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url(../images/osd-bg-1.jpg);
background-size: cover;
&:active {
background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url(../../images/osd-bg-1-grid.png), url(../../images/osd-bg-1.jpg);
background-size: cover;
}
}
input[type='checkbox'] {
&:after {

View file

@ -214,6 +214,7 @@
margin: 0;
flex: 1 1 auto;
flex-wrap: nowrap;
border: 1px solid transparent;
img {
flex: 1 1 auto;
}
@ -221,6 +222,12 @@
.char[draggable="true"] {
cursor: move;
}
&:active {
.char {
border: 1px dashed rgba(55, 55, 55, 0.5) ;
}
}
}
.char.mouseover {
background: rgba(255,255,255,0.4);
@ -307,24 +314,7 @@
select.osd-variant {
max-width: 100%;
}
.video-pal {
.preview {
&:active {
background: url(../../images/osd-bg-1-grid-pal.png), url(../../images/osd-bg-1.jpg);
background-size: 100% 100%, cover;
background-repeat: no-repeat;
}
}
}
.video-ntsc {
.preview {
&:active {
background: url(../../images/osd-bg-1-grid-ntsc.png), url(../../images/osd-bg-1.jpg);
background-size: 100% 100%, cover;
background-repeat: no-repeat;
}
}
}
.alarms {
label {
display: block;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View file

@ -138,6 +138,9 @@ const MSPCodes = {
MSP_BEEPER_CONFIG: 184,
MSP_SET_BEEPER_CONFIG: 185,
MSP_SET_OSD_CANVAS: 188,
MSP_OSD_CANVAS: 189,
MSP_SET_RAW_RC: 200,
MSP_SET_RAW_GPS: 201, // Not used
MSP_SET_PID: 202,

View file

@ -1694,6 +1694,12 @@ MspHelper.prototype.process_data = function(dataHandler) {
case MSPCodes.MSP_SET_FAILSAFE_CONFIG:
console.log('Failsafe config saved');
break;
case MSPCodes.MSP_OSD_CANVAS:
OSD.data.VIDEO_COLS['HD'] = data.readU8();
OSD.data.VIDEO_ROWS['HD'] = data.readU8();
OSD.data.VIDEO_BUFFER_CHARS['HD'] = OSD.data.VIDEO_COLS['HD'] * OSD.data.VIDEO_ROWS['HD'];
console.log(`Canvas ${OSD.data.VIDEO_COLS['HD']} x ${OSD.data.VIDEO_ROWS['HD']}`);
break;
case MSPCodes.MSP_OSD_CONFIG:
break;
case MSPCodes.MSP_SET_OSD_CONFIG:

View file

@ -45,6 +45,7 @@ SYM.loadSymbols = function() {
SYM.ARROW_SOUTH = 0x60;
SYM.ARROW_EAST = 0x64;
SYM.ARROW_SMALL_UP = 0x75;
SYM.ARROW_SMALL_RIGHT = 0x77;
SYM.HEADING_LINE = 0x1D;
SYM.HEADING_DIVIDED_LINE = 0x1C;
SYM.HEADING_N = 0x18;
@ -106,7 +107,6 @@ FONT.constants = {
MAX_NVM_FONT_CHAR_FIELD_SIZE: 64,
CHAR_HEIGHT: 18,
CHAR_WIDTH: 12,
LINE: 30,
},
COLORS: {
// black
@ -292,6 +292,21 @@ OSD.initData = function() {
preview: [],
tooltips: [],
osd_profiles: {},
VIDEO_COLS: {
PAL: 30,
NTSC: 30,
HD: 53,
},
VIDEO_ROWS: {
PAL: 16,
NTSC: 13,
HD: 20,
},
VIDEO_BUFFER_CHARS: {
PAL: 480,
NTSC: 390,
HD: 1590,
},
};
};
OSD.initData();
@ -542,6 +557,8 @@ OSD.formatPidsPreview = function(axis) {
OSD.loadDisplayFields = function() {
let videoType = OSD.constants.VIDEO_TYPES[OSD.data.video_system];
// All display fields, from every version, do not remove elements, only add!
OSD.ALL_DISPLAY_FIELDS = {
MAIN_BATT_VOLTAGE: {
@ -632,11 +649,7 @@ OSD.loadDisplayFields = function() {
text: 'osdTextElementCrosshairs',
desc: 'osdDescElementCrosshairs',
defaultPosition() {
let position = 193;
if (OSD.constants.VIDEO_TYPES[OSD.data.video_system] !== 'NTSC') {
position += FONT.constants.SIZES.LINE;
}
return position;
return (OSD.data.VIDEO_COLS[videoType] >> 1) + ((OSD.data.VIDEO_ROWS[videoType] >> 1) - 2) * OSD.data.VIDEO_COLS[videoType] - 2;
},
draw_order: 40,
positionable() {
@ -651,11 +664,7 @@ OSD.loadDisplayFields = function() {
text: 'osdTextElementArtificialHorizon',
desc: 'osdDescElementArtificialHorizon',
defaultPosition() {
let position = 74;
if (OSD.constants.VIDEO_TYPES[OSD.data.video_system] !== 'NTSC') {
position += FONT.constants.SIZES.LINE;
}
return position;
return (OSD.data.VIDEO_COLS[videoType] >> 1) + ((OSD.data.VIDEO_ROWS[videoType] >> 1) - 5) * OSD.data.VIDEO_COLS[videoType] - 1;
},
draw_order: 10,
positionable() {
@ -688,11 +697,7 @@ OSD.loadDisplayFields = function() {
text: 'osdTextElementHorizonSidebars',
desc: 'osdDescElementHorizonSidebars',
defaultPosition() {
let position = 194;
if (OSD.constants.VIDEO_TYPES[OSD.data.video_system] !== 'NTSC') {
position += FONT.constants.SIZES.LINE;
}
return position;
return (OSD.data.VIDEO_COLS[videoType] >> 1) + ((OSD.data.VIDEO_ROWS[videoType] >> 1) - 2) * OSD.data.VIDEO_COLS[videoType] - 1;
},
draw_order: 50,
positionable() {
@ -1369,6 +1374,107 @@ OSD.loadDisplayFields = function() {
positionable: true,
preview: 'AUX',
},
SYS_GOGGLE_VOLTAGE: {
name: 'SYS_GOGGLE_VOLTAGE',
text: 'osdTextElementSysGoggleVoltage',
desc: 'osdDescElementSysGoggleVoltage',
defaultPosition: -1,
draw_order: 485,
positionable: true,
preview: 'G 16.8V',
},
SYS_VTX_VOLTAGE: {
name: 'SYS_VTX_VOLTAGE',
text: 'osdTextElementSysVtxVoltage',
desc: 'osdDescElementSysVtxVoltage',
defaultPosition: -1,
draw_order: 490,
positionable: true,
preview: 'A 12.6V',
},
SYS_BITRATE: {
name: 'SYS_BITRATE',
text: 'osdTextElementSysBitrate',
desc: 'osdDescElementSysBitrate',
defaultPosition: -1,
draw_order: 495,
positionable: true,
preview: '50MBPS',
},
SYS_DELAY: {
name: 'SYS_DELAY',
text: 'osdTextElementSysDelay',
desc: 'osdDescElementSysDelay',
defaultPosition: -1,
draw_order: 500,
positionable: true,
preview: '24.5MS',
},
SYS_DISTANCE: {
name: 'SYS_DISTANCE',
text: 'osdTextElementSysDistance',
desc: 'osdDescElementSysDistance',
defaultPosition: -1,
draw_order: 505,
positionable: true,
preview: `10${FONT.symbol(SYM.METRE)}`,
},
SYS_LQ: {
name: 'SYS_LQ',
text: 'osdTextElementSysLQ',
desc: 'osdDescElementSysLQ',
defaultPosition: -1,
draw_order: 510,
positionable: true,
preview: `G${FONT.symbol(SYM.LINK_QUALITY)}100`,
},
SYS_GOGGLE_DVR: {
name: 'SYS_GOGGLE_DVR',
text: 'osdTextElementSysGoggleDVR',
desc: 'osdDescElementSysGoggleDVR',
defaultPosition: -1,
draw_order: 515,
positionable: true,
preview: `${FONT.symbol(SYM.ARROW_SMALL_RIGHT)}G DVR 8.4G`,
},
SYS_VTX_DVR: {
name: 'SYS_VTX_DVR',
text: 'osdTextElementSysVtxDVR',
desc: 'osdDescElementSysVtxDVR',
defaultPosition: -1,
draw_order: 520,
positionable: true,
preview: `${FONT.symbol(SYM.ARROW_SMALL_RIGHT)}A DVR 1.6G`,
},
SYS_WARNINGS: {
name: 'SYS_WARNINGS',
text: 'osdTextElementSysWarnings',
desc: 'osdDescElementSysWarnings',
defaultPosition: -1,
draw_order: 525,
positionable: true,
preview: 'VTX WARNINGS',
},
SYS_VTX_TEMP: {
name: 'SYS_VTX_TEMP',
text: 'osdTextElementSysVtxTemp',
desc: 'osdDescElementSysVtxTemp',
defaultPosition: -1,
draw_order: 530,
positionable: true,
preview(osdData) {
return `V${OSD.generateTemperaturePreview(osdData, 45)}`;
},
},
SYS_FAN_SPEED: {
name: 'SYS_FAN_SPEED',
text: 'osdTextElementSysFanSpeed',
desc: 'osdDescElementSysFanSpeed',
defaultPosition: -1,
draw_order: 535,
positionable: true,
preview: `F${FONT.symbol(SYM.TEMPERATURE)}5`,
},
};
};
@ -1379,15 +1485,8 @@ OSD.constants = {
'AUTO',
'PAL',
'NTSC',
'HD',
],
VIDEO_LINES: {
PAL: 16,
NTSC: 13,
},
VIDEO_BUFFER_CHARS: {
PAL: 480,
NTSC: 390,
},
UNIT_TYPES: [
'IMPERIAL',
'METRIC',
@ -1805,6 +1904,17 @@ OSD.chooseFields = function() {
F.WH_DRAWN,
F.AUX_VALUE,
F.READY_MODE,
F.SYS_GOGGLE_VOLTAGE,
F.SYS_VTX_VOLTAGE,
F.SYS_BITRATE,
F.SYS_DELAY,
F.SYS_DISTANCE,
F.SYS_LQ,
F.SYS_GOGGLE_DVR,
F.SYS_VTX_DVR,
F.SYS_WARNINGS,
F.SYS_VTX_TEMP,
F.SYS_FAN_SPEED,
]);
}
}
@ -1968,15 +2078,13 @@ OSD.updateDisplaySize = function() {
if (videoType === 'AUTO') {
videoType = 'PAL';
}
// compute the size
OSD.data.displaySize = {
x: FONT.constants.SIZES.LINE,
y: OSD.constants.VIDEO_LINES[videoType],
x: OSD.data.VIDEO_COLS[videoType],
y: OSD.data.VIDEO_ROWS[videoType],
total: null,
};
// Adjust css background grid
const previewLayoutElement = $(".tab-osd .display-layout");
videoType === 'PAL' ? previewLayoutElement.addClass('video-pal').removeClass('video-ntsc') : previewLayoutElement.addClass('video-ntsc').removeClass('video-pal');
};
OSD.drawByOrder = function(selectedPosition, field, charCode, x, y) {
@ -2017,9 +2125,15 @@ OSD.msp = {
const defaultPosition = typeof (c.defaultPosition) === 'function' ? c.defaultPosition() : c.defaultPosition;
displayItem.positionable = positionable;
OSD.updateDisplaySize();
if (semver.gte(FC.CONFIG.apiVersion, "1.21.0")) {
// size * y + x
displayItem.position = positionable ? FONT.constants.SIZES.LINE * ((bits >> 5) & 0x001F) + (bits & 0x001F) : defaultPosition;
const xpos = ((bits >> 5) & 0x0020) | (bits & 0x001F);
const ypos = (bits >> 5) & 0x001F;
displayItem.position = positionable ? OSD.data.displaySize.x * ypos + xpos : defaultPosition;
displayItem.isVisible = [];
for (let osd_profile = 0; osd_profile < OSD.getNumberOfProfiles(); osd_profile++) {
@ -2056,8 +2170,10 @@ OSD.msp = {
packed_visible |= isVisible[osd_profile] ? OSD.constants.VISIBLE << osd_profile : 0;
}
const variantSelected = (variant << 14);
const xpos = position % OSD.data.displaySize.x;
const ypos = (position - xpos) / OSD.data.displaySize.x;
return packed_visible | variantSelected | (((position / FONT.constants.SIZES.LINE) & 0x001F) << 5) | (position % FONT.constants.SIZES.LINE);
return packed_visible | variantSelected | ((ypos & 0x001F) << 5) | ((xpos & 0x0020) << 5) | (xpos & 0x001F);
} else {
const realPosition = position === -1 ? 0 : position;
return isVisible[0] ? realPosition : -1;
@ -2483,7 +2599,9 @@ OSD.GUI.preview = {
const displayItem = OSD.data.displayItems[fieldId];
let position = $(this).removeAttr('style').data('position');
const cursor = position;
const cursorX = cursor % FONT.constants.SIZES.LINE;
const cursorX = cursor % OSD.data.displaySize.x;
console.log(`cursorX=${cursorX}`);
if (displayItem.preview.constructor === Array) {
console.log(`Initial Drop Position: ${position}`);
@ -2491,14 +2609,14 @@ OSD.GUI.preview = {
const y = parseInt(ev.dataTransfer.getData('y'));
console.log(`XY Co-ords: ${x}-${y}`);
position -= x;
position -= (y * FONT.constants.SIZES.LINE);
position -= (y * OSD.data.displaySize.x);
console.log(`Calculated Position: ${position}`);
}
if (!displayItem.ignoreSize) {
if (displayItem.preview.constructor !== Array) {
// Standard preview, string type
const overflowsLine = FONT.constants.SIZES.LINE - ((position % FONT.constants.SIZES.LINE) + displayItem.preview.length);
const overflowsLine = OSD.data.displaySize.x - ((position % OSD.data.displaySize.x) + displayItem.preview.length);
if (overflowsLine < 0) {
position += overflowsLine;
}
@ -2506,34 +2624,34 @@ OSD.GUI.preview = {
// Advanced preview, array type
const arrayElements = displayItem.preview;
const limits = OSD.searchLimitsElement(arrayElements);
const selectedPositionX = position % FONT.constants.SIZES.LINE;
let selectedPositionY = Math.trunc(position / FONT.constants.SIZES.LINE);
const selectedPositionX = position % OSD.data.displaySize.x;
let selectedPositionY = Math.trunc(position / OSD.data.displaySize.x);
if (arrayElements[0].constructor === String) {
if (position < 0 ) {
return;
}
if (selectedPositionX > cursorX) { // TRUE -> Detected wrap around
position += FONT.constants.SIZES.LINE - selectedPositionX;
position += OSD.data.displaySize.x - selectedPositionX;
selectedPositionY++;
} else if (selectedPositionX + limits.maxX > FONT.constants.SIZES.LINE) { // TRUE -> right border of the element went beyond left edge of screen.
position -= selectedPositionX + limits.maxX - FONT.constants.SIZES.LINE;
} else if (selectedPositionX + limits.maxX > OSD.data.displaySize.x) { // TRUE -> right border of the element went beyond left edge of screen.
position -= selectedPositionX + limits.maxX - OSD.data.displaySize.x;
}
if (selectedPositionY < 0 ) {
position += Math.abs(selectedPositionY) * FONT.constants.SIZES.LINE;
position += Math.abs(selectedPositionY) * OSD.data.displaySize.x;
} else if ((selectedPositionY + limits.maxY ) > OSD.data.displaySize.y) {
position -= (selectedPositionY + limits.maxY - OSD.data.displaySize.y) * FONT.constants.SIZES.LINE;
position -= (selectedPositionY + limits.maxY - OSD.data.displaySize.y) * OSD.data.displaySize.x;
}
} else {
if ((limits.minX < 0) && ((selectedPositionX + limits.minX) < 0)) {
position += Math.abs(selectedPositionX + limits.minX);
} else if ((limits.maxX > 0) && ((selectedPositionX + limits.maxX) >= FONT.constants.SIZES.LINE)) {
position -= (selectedPositionX + limits.maxX + 1) - FONT.constants.SIZES.LINE;
} else if ((limits.maxX > 0) && ((selectedPositionX + limits.maxX) >= OSD.data.displaySize.x)) {
position -= (selectedPositionX + limits.maxX + 1) - OSD.data.displaySize.x;
}
if ((limits.minY < 0) && ((selectedPositionY + limits.minY) < 0)) {
position += Math.abs(selectedPositionY + limits.minY) * FONT.constants.SIZES.LINE;
position += Math.abs(selectedPositionY + limits.minY) * OSD.data.displaySize.x;
} else if ((limits.maxY > 0) && ((selectedPositionY + limits.maxY) >= OSD.data.displaySize.y)) {
position -= (selectedPositionY + limits.maxY - OSD.data.displaySize.y + 1) * FONT.constants.SIZES.LINE;
position -= (selectedPositionY + limits.maxY - OSD.data.displaySize.y + 1) * OSD.data.displaySize.x;
}
}
}
@ -2550,6 +2668,11 @@ OSD.GUI.preview = {
},
};
async function getCanvas() {
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_45)) {
return MSP.promise(MSPCodes.MSP_OSD_CANVAS);
}
}
const osd = {
analyticsChanges: {},
@ -2648,6 +2771,9 @@ osd.initialize = function(callback) {
// 2 way binding... sorta
function updateOsdView() {
// ask for the OSD canvas data
getCanvas();
// ask for the OSD config data
MSP.promise(MSPCodes.MSP_OSD_CONFIG)
.then(function(info) {
@ -3077,7 +3203,6 @@ osd.initialize = function(callback) {
// Insert in alphabetical order, with unknown fields at the end
$field.name = field.name;
insertOrdered($displayFields, $field);
}
GUI.switchery();
@ -3144,7 +3269,7 @@ osd.initialize = function(callback) {
ctx.drawImage(img, j * 12, i * 18);
}
}
selectedPosition = selectedPosition - element.length + FONT.constants.SIZES.LINE;
selectedPosition = selectedPosition - element.length + OSD.data.displaySize.x;
} else {
const limits = OSD.searchLimitsElement(arrayElements);
let offsetX = 0;
@ -3157,7 +3282,7 @@ osd.initialize = function(callback) {
}
// Add the character to the preview
const charCode = element.sym;
OSD.drawByOrder(selectedPosition + element.x + element.y * FONT.constants.SIZES.LINE, field, charCode, element.x, element.y);
OSD.drawByOrder(selectedPosition + element.x + element.y * OSD.data.displaySize.x, field, charCode, element.x, element.y);
// Image used when "dragging" the element
if (field.positionable) {
const img = new Image();