From efcc46a747d08d65392797af634c29ab057f3105 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 28 Nov 2020 07:14:06 +0100 Subject: [PATCH] VOR single channel: reworked as single channel and simple GUI --- plugins/channelrx/CMakeLists.txt | 3 +- plugins/channelrx/demodvorsc/CMakeLists.txt | 7 +- plugins/channelrx/demodvorsc/icons.qrc | 6 - .../channelrx/demodvorsc/icons/compass.png | Bin 547 -> 0 bytes plugins/channelrx/demodvorsc/icons/vor.png | Bin 365 -> 0 bytes plugins/channelrx/demodvorsc/map.qrc | 10 - .../channelrx/demodvorsc/map/MapStation.qml | 40 - plugins/channelrx/demodvorsc/map/VOR-DME.png | Bin 652 -> 0 bytes plugins/channelrx/demodvorsc/map/VOR.png | Bin 415 -> 0 bytes plugins/channelrx/demodvorsc/map/VORTAC.png | Bin 847 -> 0 bytes plugins/channelrx/demodvorsc/map/antenna.png | Bin 1328 -> 0 bytes plugins/channelrx/demodvorsc/map/map.qml | 115 -- plugins/channelrx/demodvorsc/navaid.h | 368 ------ plugins/channelrx/demodvorsc/readme.md | 50 +- plugins/channelrx/demodvorsc/vordemodsc.cpp | 11 - .../demodvorsc/vordemodscbaseband.cpp | 149 +-- .../channelrx/demodvorsc/vordemodscbaseband.h | 60 +- .../channelrx/demodvorsc/vordemodscgui.cpp | 1095 +---------------- plugins/channelrx/demodvorsc/vordemodscgui.h | 199 +-- plugins/channelrx/demodvorsc/vordemodscgui.ui | 585 +++++---- .../channelrx/demodvorsc/vordemodscreport.cpp | 1 - .../channelrx/demodvorsc/vordemodscreport.h | 46 +- .../demodvorsc/vordemodscsettings.cpp | 22 +- .../channelrx/demodvorsc/vordemodscsettings.h | 22 +- .../channelrx/demodvorsc/vordemodscsink.cpp | 67 +- plugins/channelrx/demodvorsc/vordemodscsink.h | 15 +- 26 files changed, 462 insertions(+), 2409 deletions(-) delete mode 100644 plugins/channelrx/demodvorsc/icons.qrc delete mode 100644 plugins/channelrx/demodvorsc/icons/compass.png delete mode 100644 plugins/channelrx/demodvorsc/icons/vor.png delete mode 100644 plugins/channelrx/demodvorsc/map.qrc delete mode 100644 plugins/channelrx/demodvorsc/map/MapStation.qml delete mode 100644 plugins/channelrx/demodvorsc/map/VOR-DME.png delete mode 100644 plugins/channelrx/demodvorsc/map/VOR.png delete mode 100644 plugins/channelrx/demodvorsc/map/VORTAC.png delete mode 100644 plugins/channelrx/demodvorsc/map/antenna.png delete mode 100644 plugins/channelrx/demodvorsc/map/map.qml delete mode 100644 plugins/channelrx/demodvorsc/navaid.h diff --git a/plugins/channelrx/CMakeLists.txt b/plugins/channelrx/CMakeLists.txt index 44f68d909..36623f77b 100644 --- a/plugins/channelrx/CMakeLists.txt +++ b/plugins/channelrx/CMakeLists.txt @@ -3,8 +3,8 @@ project(demod) if (Qt5Quick_FOUND AND Qt5QuickWidgets_FOUND AND Qt5Positioning_FOUND) add_subdirectory(demodadsb) add_subdirectory(demodvor) - add_subdirectory(demodvorsc) endif() + add_subdirectory(demodam) add_subdirectory(demodbfm) add_subdirectory(demodnfm) @@ -15,6 +15,7 @@ add_subdirectory(localsink) add_subdirectory(filesink) add_subdirectory(freqtracker) add_subdirectory(demodchirpchat) +add_subdirectory(demodvorsc) if(LIBDSDCC_FOUND AND LIBMBE_FOUND) add_subdirectory(demoddsd) diff --git a/plugins/channelrx/demodvorsc/CMakeLists.txt b/plugins/channelrx/demodvorsc/CMakeLists.txt index 977a0ea6e..c874319b7 100644 --- a/plugins/channelrx/demodvorsc/CMakeLists.txt +++ b/plugins/channelrx/demodvorsc/CMakeLists.txt @@ -20,7 +20,6 @@ set(vorsc_HEADERS vordemodscreport.h ) - include_directories( ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client ) @@ -30,18 +29,14 @@ if(NOT SERVER_MODE) ${vorsc_SOURCES} vordemodscgui.cpp vordemodscgui.ui - map.qrc - icons.qrc ) set(vorsc_HEADERS ${vorsc_HEADERS} vordemodscgui.h - navaid.h - ../demodadsb/csv.h ) set(TARGET_NAME demodvorsc) - set(TARGET_LIB "Qt5::Widgets" Qt5::Quick Qt5::QuickWidgets Qt5::Positioning) + set(TARGET_LIB "Qt5::Widgets") set(TARGET_LIB_GUI "sdrgui") set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) else() diff --git a/plugins/channelrx/demodvorsc/icons.qrc b/plugins/channelrx/demodvorsc/icons.qrc deleted file mode 100644 index ca8be748f..000000000 --- a/plugins/channelrx/demodvorsc/icons.qrc +++ /dev/null @@ -1,6 +0,0 @@ - - - icons/compass.png - icons/vor.png - - diff --git a/plugins/channelrx/demodvorsc/icons/compass.png b/plugins/channelrx/demodvorsc/icons/compass.png deleted file mode 100644 index 1415a50f033dafbc83ae6ca8c0e5d276d37556cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 547 zcmV+;0^I$HP)lBj5O;%&;8V`&21XZ1M)0X; zcLO&QI2*XP9^DC85O@R_mHxB>1j`i3C-JL05y2lu319@!uNB#>=*A4foSp+pvFiQ~`M5Gpx-0gQF(zySe|H@lLx+3M3h>Q~gJwZ}> zL78d^pd{mih-85x;eJY2-feWSl*z7|@#>yA=M5w{RO{Bm6hO`CuPu&N(mcYo$)u-l zys13EkT}#72o=A&|APZNCNg%@8-QZMd2otaDzmlB$nxaSI3Fp7sp!$G6vFaNEpd95c!(|I(9*Ro&wsZ zt5XGajb^ib5}m+o5LpP5LwH>;a(dRNH4b*1jq%g!fdgU^=ZV lOpdj$3RP+^i1p7F06#cXhKHzSdNKe2002ovPDHLkV1jo8=5nZU6uQA4x<(R5*>bRlyB|Fbq6U2Vesjp(8LtMqmU+V1zz803&n+MxcKl zjKBt01vxEHlO~{AI)O-hj&06KNJ6n1Km}mZ78T0?v;b~jOE$m;8Oc$e+Ip0aw)sY~ ziXeBW#Mc|(O0qvd?$8UzZh($tc_Kh59Pe-Gq8$dn2%rmWtY)2X7_G@Rz2d~_9g1a{ zT?P$+heSVXqi{7Y0~hhvZ<&*91$n@4J68naU;$?}CsX%$#1CpTK+E{9OlLnIFVz+$={$C-dI~G;q(1aD2v% zFTDf!Ds#Fy$y(-drjHws%wumf)#sud($>(5ua6jhg=Db4!vVYim$vA - - map/map.qml - map/MapStation.qml - map/antenna.png - map/VOR.png - map/VOR-DME.png - map/VORTAC.png - - diff --git a/plugins/channelrx/demodvorsc/map/MapStation.qml b/plugins/channelrx/demodvorsc/map/MapStation.qml deleted file mode 100644 index a69346e46..000000000 --- a/plugins/channelrx/demodvorsc/map/MapStation.qml +++ /dev/null @@ -1,40 +0,0 @@ -import QtQuick 2.12 -import QtLocation 5.12 -import QtPositioning 5.12 - -MapQuickItem { - id: station - property string stationName // Name of the station, E.g. Home - - coordinate: QtPositioning.coordinate(51.5, 0.125) // Location of the antenna (QTH) - London - zoomLevel: 11 - - anchorPoint.x: image.width/2 - anchorPoint.y: image.height/2 - - sourceItem: Grid { - columns: 1 - Grid { - horizontalItemAlignment: Grid.AlignHCenter - layer.enabled: true - layer.smooth: true - Image { - id: image - source: "antenna.png" - } - Rectangle { - id: bubble - color: "lightblue" - border.width: 1 - width: text.width * 1.3 - height: text.height * 1.3 - radius: 5 - Text { - id: text - anchors.centerIn: parent - text: stationName - } - } - } - } -} diff --git a/plugins/channelrx/demodvorsc/map/VOR-DME.png b/plugins/channelrx/demodvorsc/map/VOR-DME.png deleted file mode 100644 index c2e2c412da4ada7c9f510676475323ebd2c218e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 652 zcmV;70(1R|P)00009a7bBm000C; z000C;0U7^T5C8xIK1oDDR9J=0SM5>bKn#@_{vT9u?toAM>401Zxe7=Lrh{Aup@UEX z&_U9H>3|O2&C9*@?Ko?jEb9!K*?ijCr?>Kx7g%PPX=GROs%COr6gd?C=EAViD*GZ5>THBkSS;Xkc?O&LYcWH zjie61psyMt zL`7$9GC|F|pt2-D4!WR_<16ylgr{$onZGH;f6sBGgs+0n5aG#5u3$boOy#m(hvN&9 zu%~kXNy#6HkiZs-atmOa;iHlyS$Id3Wb<=MLB!HV$5yvYu@!Z9MmPhIY$v8N733E) z`=xZ4$F+|F_S4;q_fR7i>TP@MEhyEY$FWwmq^S_!beqXkmtf~8$_kwOJ*jt3gHo$X z)DL~FYC#L^B-7Po~P*3B?0#; z=1Cix`wsJ&#;uYTB%pVm>;)x>2=iGHiHIa%u)Dpq|HEg>2<8WCn!A^nl*vB3h^0G= m$&npuFaIe$mVeF6c7Fl00;$kZ1o_(l00004=OGI diff --git a/plugins/channelrx/demodvorsc/map/VOR.png b/plugins/channelrx/demodvorsc/map/VOR.png deleted file mode 100644 index 0d6949fde5025be971f69c6b0572951528f9c26a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 415 zcmV;Q0bu@#P)00009a7bBm000C^ z000C^0ljtSLMVhnI0%Dq5DH-s3ZW1NVGzdP zO_|*3*5>0lX_iHbbh=BNm*;24IgreD00#i<)IS02N^6QPoNOX;1#l&~W(OLYh+G0N zl1p}Ufu%6{!?HU|2e4PR4%u}xfOZP#)C17Q?yAhYKouuDK@~TfnrNcA0ob=Js)Q~g zZ>qPvAlpQr4xq>`j@Hm^Wb3xhzQ~k_yaTxONC5`6;vT8-^^-&&*g8gqo*FO!*h7?O zKMn*IQpEBLl%@d2L^lZ3X&YR(mmxgQPJ@dYWXlPl7*>7urvROMUqM8}++gd+TMz!M zG_)p4npda2b4W)o&(1~OHlO>Z6#6|%kHOX+O$nSad2-fcW}}ZkKal4yT{vrzm)${M zmh9y)C%8CNMMXz(*I%)$lbxXam(eZFqp$7%?+1qh9{VeZ#1|e4Q=R*2MxFow002ov JPDHLkV1ja&s@DJj diff --git a/plugins/channelrx/demodvorsc/map/VORTAC.png b/plugins/channelrx/demodvorsc/map/VORTAC.png deleted file mode 100644 index f7df0a0b38a174b07fa25f8c808cc57200f27fbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 847 zcmV-V1F-ywP)asXkb9dy4iWN@7~NV z1`(}w0Am0@0361CyA21>2QULLh~^#vb_jq<{t>{zErIF)_y%CX`OIUvtP9oxFbTjF zKJb6v5~u?J!l|sMT2347Y^EG*R?<9faYSje`-JGfw6kO+%RB+=2aAs7E zP_WJyb>iYU=#4q&yokI-y7@rA0K5e7LGgSSz!L!bK8?g@{{nbSM3+n7CWy#g_Z4wT z5fQ2Pl$3m9uy&^sT%w57`9;S2)?#JMS$|RoP9q{>Sr!89Wv;&@E=ACA643-bHEIJF znYr9dE>mGpT%+o7n zkf#|5R2mUY0lZK??s0s-9tcV@e7Xq{;n-dU&j6hBXVEOG5SRIiS_Ut(G+-x#5%kW5ut0T_%RF|~ zAniNh4oE1~7f|V3`0j<1%L)Zm;DQDI0r(>Py#(A@!uo#gKPe-g7#uHfl(2Jc3tE;6 z^)vOvp9GE<8YR$a%zdW{+(a_6@oB~Py?%183UI~INGb+Zrw#uOw}tDy18|v|rwUw& zCR6FK%oOdji*L+IeNJ`DwsMCeF`oVWv@s&0u5wI@nsd&tF(smoDuAXUT#&9*T$J$( z`Bz^Xs&=k0=iG=3q^oRwT+a-DlXXl)eRZJB>K1a}HQu!o2A6jaYLLirins{kb*u`O zMazIfCq&fC3T91pqiPr8BI;e6j$^iSg*oRw19-?Ypw=D$cuhn{7CG%(1Yvs*;2D5> znQ>lOCA4Q1h%a<3?6*}ymf-H3?Z7qFcHo+7J8(_49k{024qQ{sa&W#rhZ)%NKgwtg Zz<&Xuil2FEQ*!_S002ovPDHLkV1gS3cHjU2 diff --git a/plugins/channelrx/demodvorsc/map/antenna.png b/plugins/channelrx/demodvorsc/map/antenna.png deleted file mode 100644 index f13c9188161f77915ca18139b8b4884b60be5511..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1328 zcmV-01<(44P)X1ieW_ zK~!ko?V3$&RYerXe|=O+MLxy0kf^jOh7A@gARp=mn;4U#prS+*AYnlwYzWar(UtO< zE=&w53tb2ZA`QV60n|j$HjO5N8WSis0;N!dmMS3Hj*I>$oKEh0l=j}|CE+AD@8&-< zXXebD&$$n7aVm(JUolV(Oa&$YbxJv~9#RU^noW7MJV!9oPtrOT!7= ztHr!zc%1|G0lxufgpCI(bUG!%I)U}TCg7j{W7lT^ZDwcp0&{>&jxj@kCx9=^xG#Y* z`Cj}2>9zxoYf(2#PXW#1@hfnDz83$e(e-bPN`$Iep%a;-_b7F}TZZj`pMjA{FTOn{UNyK@t($AF-Wur2KwQ#HmaLR_dt?+Md^#v%rXm&G;k<&*Olb~xba z)#$-E1$aYDw;L>@-Rd;aw+!eOR&M%M0GDLIrDlAmPSv{39h$3O!7Cg(iKl^o#JaI0YzH@Cm)cd|^KZZm-689;f#j(_U!Oi{}jffQiPjQ+wp!thfd}$&WkV7I%oFogqw@d{OOLx1hDBZ&%V~WVZs?lR5gW(_&UA z7jnSb8&qf4^h{2=sCiQhItSdBrO(&uGYy!o_ph?_ogiKgSe~!4@*_>SBunAmr9d=P z(fYEySYbr}m&L0tpGAEJn2{x8tc*y~e1i5!7G6~Y%|0^&6re{gusfpdas4(JtRZL% zd3~RSS7HL>q8a*aG+1NM7ON^GC4f!FF6TIbRMBXuf>WYb1xx`hn0#nVngyuVLVezJ zR*%K1)2u>Xeco5~R-$LSu-NS&ZUJWM_n6WPPt#3>mRdU~Fe$W8;P&aU*!iTd4+nE| zXRlEjRCV1+?d-ZCg{?7GG>GxoS+{YBGXdP+N!k7N#0& zK;Lg(<+@wS(0iYnIy>+;&5P*$UhI_2Et7Zh-_#DO2y(z@X%fJHJNUx{t;(RtIc{zv z{5P{$qUS(q1Ta@V{$qo;18>py4%BC=?{WR27@bB3a6rjkUg;ff{I`PsKI;d%h{8b# muuZOTuz8KsCBbdFCFwsJE06r64B5H>0000 11) { - station.zoomLevel = zoomLevel - vorZoomLevel = zoomLevel - } else { - station.zoomLevel = 11 - vorZoomLevel = 11 - } - } - - } - - Component { - id: vorRadialComponent - MapPolyline { - line.width: 2 - line.color: 'gray' - path: vorRadial - } - } - - Component { - id: vorComponent - MapQuickItem { - id: vor - anchorPoint.x: image.width/2 - anchorPoint.y: bubble.height/2 - coordinate: position - zoomLevel: vorZoomLevel - - sourceItem: Grid { - columns: 1 - Grid { - horizontalItemAlignment: Grid.AlignHCenter - verticalItemAlignment: Grid.AlignVCenter - columnSpacing: 5 - layer.enabled: true - layer.smooth: true - Image { - id: image - source: vorImage - MouseArea { - anchors.fill: parent - hoverEnabled: true - onDoubleClicked: (mouse) => { - selected = !selected - } - } - } - Rectangle { - id: bubble - color: bubbleColour - border.width: 1 - width: text.width + 5 - height: text.height + 5 - radius: 5 - Text { - id: text - anchors.centerIn: parent - text: vorData - } - MouseArea { - anchors.fill: parent - hoverEnabled: true - onDoubleClicked: (mouse) => { - selected = !selected - } - } - } - } - } - } - } - -} diff --git a/plugins/channelrx/demodvorsc/navaid.h b/plugins/channelrx/demodvorsc/navaid.h deleted file mode 100644 index f770b281c..000000000 --- a/plugins/channelrx/demodvorsc/navaid.h +++ /dev/null @@ -1,368 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Jon Beniston, M7RCE // -// // -// 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 as version 3 of the License, 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDE_NAVAID_H -#define INCLUDE_NAVAID_H - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "util/units.h" -#include "../demodadsb/csv.h" - -#define OURAIRPORTS_NAVAIDS_URL "https://ourairports.com/data/navaids.csv" - -#define OPENAIP_NAVAIDS_URL "https://www.openaip.net/customer_export_akfshb9237tgwiuvb4tgiwbf/%1_nav.aip" - -struct NavAid { - - int m_id; - QString m_ident; // 2 or 3 character ident - QString m_type; // VOR, VOR-DME or VORTAC - QString m_name; - float m_latitude; - float m_longitude; - float m_elevation; - int m_frequencykHz; - QString m_channel; - int m_range; // Nautical miles - float m_magneticDeclination; - bool m_alignedTrueNorth; // Is the VOR aligned to true North, rather than magnetic (may be the case at high latitudes) - - static QString trimQuotes(const QString s) - { - if (s.startsWith('\"') && s.endsWith('\"')) - return s.mid(1, s.size() - 2); - else - return s; - } - - int getRangeMetres() - { - return Units::nauticalMilesToIntegerMetres((float)m_range); - } - - // OpenAIP XML file - static void readNavAidsXML(QHash *navAidInfo, const QString &filename) - { - QFile file(filename); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - QXmlStreamReader xmlReader(&file); - - while(!xmlReader.atEnd() && !xmlReader.hasError()) - { - if (xmlReader.readNextStartElement()) - { - if (xmlReader.name() == "NAVAID") - { - QStringRef typeRef = xmlReader.attributes().value("TYPE"); - if ((typeRef == QLatin1String("VOR")) - || (typeRef== QLatin1String("VOR-DME")) - || (typeRef == QLatin1String("VORTAC"))) - { - QString type = typeRef.toString(); - int identifier = 0; - QString name; - QString id; - float lat = 0.0f; - float lon = 0.0f; - float elevation = 0.0f; - int frequency = 0; - QString channel; - int range = 25; - float declination = 0.0f; - bool alignedTrueNorth = false; - while(xmlReader.readNextStartElement()) - { - if (xmlReader.name() == QLatin1String("IDENTIFIER")) - identifier = xmlReader.readElementText().toInt(); - else if (xmlReader.name() == QLatin1String("NAME")) - name = xmlReader.readElementText(); - else if (xmlReader.name() == QLatin1String("ID")) - id = xmlReader.readElementText(); - else if (xmlReader.name() == QLatin1String("GEOLOCATION")) - { - while(xmlReader.readNextStartElement()) - { - if (xmlReader.name() == QLatin1String("LAT")) - lat = xmlReader.readElementText().toFloat(); - else if (xmlReader.name() == QLatin1String("LON")) - lon = xmlReader.readElementText().toFloat(); - else if (xmlReader.name() == QLatin1String("ELEV")) - elevation = xmlReader.readElementText().toFloat(); - else - xmlReader.skipCurrentElement(); - } - } - else if (xmlReader.name() == QLatin1String("RADIO")) - { - while(xmlReader.readNextStartElement()) - { - if (xmlReader.name() == QLatin1String("FREQUENCY")) - frequency = (int)(xmlReader.readElementText().toFloat() * 1000); - else if (xmlReader.name() == QLatin1String("CHANNEL")) - channel = xmlReader.readElementText(); - else - xmlReader.skipCurrentElement(); - } - } - else if (xmlReader.name() == QLatin1String("PARAMS")) - { - while(xmlReader.readNextStartElement()) - { - if (xmlReader.name() == QLatin1String("RANGE")) - range = xmlReader.readElementText().toInt(); - else if (xmlReader.name() == QLatin1String("DECLINATION")) - declination = xmlReader.readElementText().toFloat(); - else if (xmlReader.name() == QLatin1String("ALIGNEDTOTRUENORTH")) - alignedTrueNorth = xmlReader.readElementText() == "TRUE"; - else - xmlReader.skipCurrentElement(); - } - } - else - xmlReader.skipCurrentElement(); - } - NavAid *vor = new NavAid(); - vor->m_id = identifier; - vor->m_ident = id; - // Check idents conform to our filtering rules - if (vor->m_ident.size() < 2) - qDebug() << "Warning: VOR Ident less than 2 characters: " << vor->m_ident; - else if (vor->m_ident.size() > 3) - qDebug() << "Warning: VOR Ident greater than 3 characters: " << vor->m_ident; - vor->m_type = type; - vor->m_name = name; - vor->m_frequencykHz = frequency; - vor->m_channel = channel; - vor->m_latitude = lat; - vor->m_longitude = lon; - vor->m_elevation = elevation; - vor->m_range = range; - vor->m_magneticDeclination = declination; - vor->m_alignedTrueNorth = alignedTrueNorth; - navAidInfo->insert(identifier, vor); - } - } - } - } - - file.close(); - } - else - qDebug() << "NavAid::readNavAidsXML: Could not open " << filename << " for reading."; - } - - // Read OurAirport's NavAids CSV file - // See comments for readOSNDB - static QHash *readNavAidsDB(const QString &filename) - { - int cnt = 0; - QHash *navAidInfo = nullptr; - - // Column numbers used for the data as of 2020/10/28 - int idCol = 0; - int identCol = 2; - int typeCol = 4; - int nameCol = 3; - int frequencyCol = 5; - int latitudeCol = 6; - int longitudeCol = 7; - int elevationCol = 8; - int powerCol = 18; - - qDebug() << "NavAid::readNavAidsDB: " << filename; - - FILE *file; - QByteArray utfFilename = filename.toUtf8(); - if ((file = fopen(utfFilename.constData(), "r")) != NULL) - { - char row[2048]; - - if (fgets(row, sizeof(row), file)) - { - navAidInfo = new QHash(); - navAidInfo->reserve(15000); - - // Read header - int idx = 0; - char *p = strtok(row, ","); - while (p != NULL) - { - if (!strcmp(p, "id")) - idCol = idx; - else if (!strcmp(p, "ident")) - identCol = idx; - else if (!strcmp(p, "type")) - typeCol = idx; - else if (!strcmp(p, "name")) - nameCol = idx; - else if (!strcmp(p, "frequency_khz")) - frequencyCol = idx; - else if (!strcmp(p, "latitude_deg")) - latitudeCol = idx; - else if (!strcmp(p, "longitude_deg")) - longitudeCol = idx; - else if (!strcmp(p, "elevation_ft")) - elevationCol = idx; - else if (!strcmp(p, "power")) - powerCol = idx; - p = strtok(NULL, ","); - idx++; - } - // Read data - while (fgets(row, sizeof(row), file)) - { - int id = 0; - char *idString = NULL; - char *ident = NULL; - size_t identLen = 0; - char *type = NULL; - size_t typeLen = 0; - char *name = NULL; - size_t nameLen = 0; - char *frequencyString = NULL; - int frequency; - float latitude = 0.0f; - char *latitudeString = NULL; - size_t latitudeLen = 0; - float longitude = 0.0f; - char *longitudeString = NULL; - size_t longitudeLen = 0; - float elevation = 0.0f; - char *elevationString = NULL; - size_t elevationLen = 0; - char *power = NULL; - size_t powerLen = 0; - - char *q = row; - idx = 0; - while ((p = csvNext(&q)) != nullptr) - { - // Read strings, stripping quotes - if (idx == idCol) - { - idString = p; - idString[strlen(idString)] = '\0'; - id = strtol(idString, NULL, 10); - } - else if ((idx == identCol) && (p[0] == '\"')) - { - ident = p+1; - identLen = strlen(ident)-1; - ident[identLen] = '\0'; - } - else if ((idx == typeCol) && (p[0] == '\"')) - { - type = p+1; - typeLen = strlen(type)-1; - type[typeLen] = '\0'; - } - else if ((idx == nameCol) && (p[0] == '\"')) - { - name = p+1; - nameLen = strlen(name)-1; - name[nameLen] = '\0'; - } - if (idx == frequencyCol) - { - frequencyString = p; - frequencyString[strlen(frequencyString)] = '\0'; - frequency = strtol(frequencyString, NULL, 10); - } - else if (idx == latitudeCol) - { - latitudeString = p; - latitudeLen = strlen(latitudeString)-1; - latitudeString[latitudeLen] = '\0'; - latitude = atof(latitudeString); - } - else if (idx == longitudeCol) - { - longitudeString = p; - longitudeLen = strlen(longitudeString)-1; - longitudeString[longitudeLen] = '\0'; - longitude = atof(longitudeString); - } - else if (idx == elevationCol) - { - elevationString = p; - elevationLen = strlen(elevationString)-1; - elevationString[elevationLen] = '\0'; - elevation = atof(elevationString); - } - else if ((idx == powerCol) && (p[0] == '\"')) - { - power = p+1; - powerLen = strlen(power)-1; - power[powerLen] = '\0'; - } - idx++; - } - - // For now, we only want VORs - if (type && !strncmp(type, "VOR", 3)) - { - NavAid *vor = new NavAid(); - vor->m_id = id; - vor->m_ident = QString(ident); - // Check idents conform to our filtering rules - if (vor->m_ident.size() < 2) - qDebug() << "Warning: VOR Ident less than 2 characters: " << vor->m_ident; - else if (vor->m_ident.size() > 3) - qDebug() << "Warning: VOR Ident greater than 3 characters: " << vor->m_ident; - vor->m_type = QString(type); - vor->m_name = QString(name); - vor->m_frequencykHz = frequency; - vor->m_latitude = latitude; - vor->m_longitude = longitude; - vor->m_elevation = elevation; - if (power && !strcmp(power, "HIGH")) - vor->m_range = 100; - else if (power && !strcmp(power, "MEDIUM")) - vor->m_range = 40; - else - vor->m_range = 25; - vor->m_magneticDeclination = 0.0f; - vor->m_alignedTrueNorth = false; - navAidInfo->insert(id, vor); - cnt++; - } - } - } - fclose(file); - } - else - qDebug() << "NavAid::readNavAidsDB: Failed to open " << filename; - - qDebug() << "NavAid::readNavAidsDB: Read " << cnt << " VORs"; - - return navAidInfo; - } - -}; - -#endif // INCLUDE_NAVAID_H diff --git a/plugins/channelrx/demodvorsc/readme.md b/plugins/channelrx/demodvorsc/readme.md index d037d5930..893c97b58 100644 --- a/plugins/channelrx/demodvorsc/readme.md +++ b/plugins/channelrx/demodvorsc/readme.md @@ -1,4 +1,4 @@ -

VOR demodulator plugin

+

VOR demodulator plugin - Single channel version

Introduction

@@ -10,10 +10,12 @@ VORs also transmit a Morse code ident signal at a 1020Hz offset. This is a 2 or Some VORs also transmit an AM voice identification or information signal between 300-3kHz. -This plugin can demodulate all four signals from multiple VORs simultaneously, allowing your position to be determined and plotted on a map. It can also demodulate the Morse code ident signal and and check they are correct for each VOR. The Morse code ident and any voice signal will also be heard as audio. - Note that for aircraft, there is typically a direct line-of-sight to the VOR. This is unlikely to be the case when using an SDR on the ground. To get good results, ideally you want to be on a nice high hill or close to the VOR. +

Using it for localization

+ +Several instances of this plugin can be created to monitor multiple VORs and collate information in the VOR Localizer feature plugin. The VOR Localizer can also perform a round robin on multiple VORs with just one VOR demodulator. Please refer to ... for more information about this feature plugin +

Interface

![VOR Demodulator plugin GUI](../../../doc/img/VORDemod_plugin.png) @@ -34,14 +36,6 @@ Left click on this button to toggle audio mute for this channel. The button will If you right click on it it will open a dialog to select the audio output device. See [audio management documentation](../../../sdrgui/audio.md) for details. -

4: Download VOR Database

- -Pressing this button downloads the OpenAIP.net Navaid database, which contains the details (position, frequencies, name and ident) for each VOR. This needs to be performed at least once. - -

5: Draw Radials Adjusted for Magnetic Declination

- -When checked, radials on the map will drawn adjusted for magnetic declination. For example, if a VOR has a magnetic declination of 5 degrees, and the radial is calculated at 0 degrees, the radial will be drawn to magnetic North, i.e. -5 degress from true North. If not checked, the same radial would be drawn to true North (i.e 0 degrees), which may result in a less accurate position estimate. -

6: Morse ident threshold

This is the Morse code ident threshold, expressed as a linear signal to noise (SNR) ratio. This is effectively the signal level required for the Morse demodulator to detect a dot or dash. Setting this to low values will allow the Morse demodulator to detect weak signals, but it also increases the likelyhood that noise will incorrectly be interpreted as a signal, resulting in invalid idents being reported. @@ -53,37 +47,3 @@ This is the squelch threshold in dB. The average total power received in the sig

8: Volume

This is the volume of the audio signal from 0.0 (mute) to 10.0 (maximum). It can be varied continuously in 0.1 steps using the dial button. - -

VOR Table

- -The VOR table displays information about selected VORs. To select or deselect a VOR, double click it on the map. The information displayed includes: - -![VOR Demodulator Table](../../../doc/img/VORDemod_plugin_table.png) - -* Name - The name of the VOR. For example: 'LONDON'. -* Freq (MHz) - The center frequency the VOR transmits on in MHz. -* Offset (kHz) - This is the current difference between the VOR's center frequency and SDRangle's device center frequency. If displayed in red, the VOR is out of range and it's signal will not be able to be received. -* Ident - A 2 or 3 character identifier for the VOR. For example: 'LON'. -* Morse - The Morse code identifier for the VOR. For example: '.-.. --- -.' -* RX Ident - This contains the demodulated ident. If it matches the expected ident, it will be displayed in green, if not, it will be displayed in red. If an ident is received that is not 2 or 3 characters, it will not be displayed, but the last received ident will be displayed in yellow. -* RX Morse - This contains the demodulated Morse code ident. Colour coding is as for RX Ident. -* Radial - This contains the demodulated radial direction in degrees (unadjusted for magnetic declination). If there is a low confidence the value is correct (due to a weak signal), it will be displayed in red. -* Ref (dB) - This displays the magnitude of the received 30Hz FM reference signal in dB. -* Var (dB) - This displays the mangitude of the received 30Hz AM variable signal in dB. -* Mute - This button allows you to mute or unmute the audio from the corresponding VOR. - -

Map

- -The map displays the locations of each VOR, with an information box containing the information about the VOR, such as it's name, frequency, ident (in text and Morse), range and magnetic declination. - -To initialise the VORs on the map, first set your position using the Preferences > My position menu, then open the VOR Demodulator channel (close and reopen it, if already open). Then press the Download VOR Database button (This only needs to be performed once). The map should then display VORs in your vicinity. - -Double clicking on a VOR will select and add it to the list of VORs to demodulate. It will be added to the VOR table and will be highlighted green. Double clicking a selected VOR, will remove it from the list of VORs to demodulate and it will be removed from the VOR table. - -When a signal from a VOR is correctly being demodulated, a radial line will be drawn on the map, at the angle corresponding to the phase difference between the AM and FM 30Hz signals. Your receiver should be somewhere along this radial line. The length of the radial line is set according to the range of the VOR as recorded in the database, which is valid for aircraft at altitude. Range on the ground will be considerably less. An approximate position for the receiver is where the radial lines from two or more VORs intersect. - -![VOR Demodulator Map](../../../doc/img/VORDemod_plugin_map.png) - -

Attribution

- -Icons by Denelson83 and mamayer, via Wikimedia Commons and RULI from the Noun Project https://thenounproject.com/ diff --git a/plugins/channelrx/demodvorsc/vordemodsc.cpp b/plugins/channelrx/demodvorsc/vordemodsc.cpp index 5af8a76c9..619af8106 100644 --- a/plugins/channelrx/demodvorsc/vordemodsc.cpp +++ b/plugins/channelrx/demodvorsc/vordemodsc.cpp @@ -196,10 +196,6 @@ void VORDemodSC::applySettings(const VORDemodSCSettings& settings, bool force) reverseAPIKeys.append("identThreshold"); } - if ((m_settings.m_magDecAdjust != settings.m_magDecAdjust) || force) { - reverseAPIKeys.append("magDecAdjust"); - } - VORDemodSCBaseband::MsgConfigureVORDemodBaseband *msg = VORDemodSCBaseband::MsgConfigureVORDemodBaseband::create(settings, force); m_basebandSink->getInputMessageQueue()->push(msg); @@ -323,9 +319,6 @@ void VORDemodSC::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("identThreshold")) { settings.m_identThreshold = response.getVorDemodSettings()->getIdentThreshold(); } - if (channelSettingsKeys.contains("magDecAdjust")) { - settings.m_magDecAdjust = response.getVorDemodSettings()->getMagDecAdjust() != 0; - } } int VORDemodSC::webapiReportGet( @@ -372,7 +365,6 @@ void VORDemodSC::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& re response.getVorDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); response.getVorDemodSettings()->setIdentThreshold(settings.m_identThreshold); - response.getVorDemodSettings()->setMagDecAdjust(settings.m_magDecAdjust ? 1 : 0); } void VORDemodSC::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) @@ -479,9 +471,6 @@ void VORDemodSC::webapiFormatChannelSettings( if (channelSettingsKeys.contains("identThreshold") || force) { swgVORDemodSettings->setAudioMute(settings.m_identThreshold); } - if (channelSettingsKeys.contains("magDecAdjust") || force) { - swgVORDemodSettings->setAudioMute(settings.m_magDecAdjust ? 1 : 0); - } } void VORDemodSC::networkManagerFinished(QNetworkReply *reply) diff --git a/plugins/channelrx/demodvorsc/vordemodscbaseband.cpp b/plugins/channelrx/demodvorsc/vordemodscbaseband.cpp index 2f3747f23..a04706171 100644 --- a/plugins/channelrx/demodvorsc/vordemodscbaseband.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscbaseband.cpp @@ -28,36 +28,23 @@ MESSAGE_CLASS_DEFINITION(VORDemodSCBaseband::MsgConfigureVORDemodBaseband, Message) VORDemodSCBaseband::VORDemodSCBaseband() : - m_running(false), - m_mutex(QMutex::Recursive), m_messageQueueToGUI(nullptr), - m_basebandSampleRate(0) + m_running(false), + m_mutex(QMutex::Recursive) { qDebug("VORDemodSCBaseband::VORDemodSCBaseband"); - m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_channelizer = new DownChannelizer(&m_sink); - // FIXME: If we remove this audio stops working when this demod is closed - DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifoBug, getInputMessageQueue()); + DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue()); + m_sink.applyAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate()); } VORDemodSCBaseband::~VORDemodSCBaseband() { m_inputMessageQueue.clear(); - - for (int i = 0; i < m_sinks.size(); i++) - { - DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sinks[i]->getAudioFifo()); - delete m_sinks[i]; - } - m_sinks.clear(); - - // FIXME: If we remove this audio stops working when this demod is closed - DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifoBug); - - for (int i = 0; i < m_channelizers.size(); i++) - delete m_channelizers[i]; - m_channelizers.clear(); + DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sink.getAudioFifo()); + delete m_channelizer; } void VORDemodSCBaseband::reset() @@ -114,14 +101,12 @@ void VORDemodSCBaseband::handleData() // first part of FIFO data if (part1begin != part1end) { - for (int i = 0; i < m_channelizers.size(); i++) - m_channelizers[i]->feed(part1begin, part1end); + m_channelizer->feed(part1begin, part1end); } // second part of FIFO data (used when block wraps around) if(part2begin != part2end) { - for (int i = 0; i < m_channelizers.size(); i++) - m_channelizers[i]->feed(part2begin, part2end); + m_channelizer->feed(part2begin, part2end); } m_sampleFifo.readCommit((unsigned int) count); @@ -157,9 +142,10 @@ bool VORDemodSCBaseband::handleMessage(const Message& cmd) QMutexLocker mutexLocker(&m_mutex); DSPSignalNotification& notif = (DSPSignalNotification&) cmd; qDebug() << "VORDemodSCBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate() << " centerFrequency: " << notif.getCenterFrequency(); - m_centerFrequency = notif.getCenterFrequency(); - setBasebandSampleRate(notif.getSampleRate()); - m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(m_basebandSampleRate)); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); + m_channelizer->setBasebandSampleRate(notif.getSampleRate()); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_sink.applyAudioSampleRate(m_sink.getAudioSampleRate()); // reapply in case of channel sample rate change return true; } @@ -169,114 +155,33 @@ bool VORDemodSCBaseband::handleMessage(const Message& cmd) } } -// Calculate offset of VOR center frequency from sample source center frequency -void VORDemodSCBaseband::calculateOffset(VORDemodSCSink *sink) -{ - int frequencyOffset = sink->m_vorFrequencyHz - m_centerFrequency; - bool outOfBand = std::abs(frequencyOffset)+VORDEMOD_CHANNEL_BANDWIDTH > (m_basebandSampleRate/2); - - if (m_messageQueueToGUI != nullptr) - { - VORDemodSCReport::MsgReportFreqOffset *msg = VORDemodSCReport::MsgReportFreqOffset::create(sink->m_subChannelId, frequencyOffset, outOfBand); - m_messageQueueToGUI->push(msg); - } - - sink->m_frequencyOffset = frequencyOffset; - sink->m_outOfBand = outOfBand; -} - void VORDemodSCBaseband::applySettings(const VORDemodSCSettings& settings, bool force) { - // Remove sub-channels no longer needed - for (int i = 0; i < m_sinks.size(); i++) + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { - if (!settings.m_subChannelSettings.contains(m_sinks[i]->m_subChannelId)) - { - qDebug() << "VORDemodSCBaseband::applySettings: Removing sink " << m_sinks[i]->m_subChannelId; - VORDemodSCSink *sink = m_sinks[i]; - DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sinks[i]->getAudioFifo()); - m_sinks.removeAt(i); - delete sink; - DownChannelizer *channelizer = m_channelizers[i]; - m_channelizers.removeAt(i); - delete channelizer; - } - } - - // Add new sub channels - QHash::const_iterator itr = settings.m_subChannelSettings.begin(); - while (itr != settings.m_subChannelSettings.end()) - { - VORDemodSubChannelSettings *subChannelSettings = itr.value(); - int j; - for (j = 0; j < m_sinks.size(); j++) - { - if (subChannelSettings->m_id == m_sinks[j]->m_subChannelId) - break; - } - if (j == m_sinks.size()) - { - // Add a sub-channel sink - qDebug() << "VORDemodSCBaseband::applySettings: Adding sink " << subChannelSettings->m_id; - VORDemodSCSink *sink = new VORDemodSCSink(settings, subChannelSettings->m_id, m_messageQueueToGUI); - DownChannelizer *channelizer = new DownChannelizer(sink); - channelizer->setBasebandSampleRate(m_basebandSampleRate); - DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(sink->getAudioFifo(), getInputMessageQueue()); - sink->applyAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate()); - - m_sinks.append(sink); - m_channelizers.append(channelizer); - - calculateOffset(sink); - - channelizer->setChannelization(VORDEMOD_CHANNEL_SAMPLE_RATE, sink->m_frequencyOffset); - sink->applyChannelSettings(channelizer->getChannelSampleRate(), channelizer->getChannelFrequencyOffset(), true); - sink->applyAudioSampleRate(sink->getAudioSampleRate()); - } - ++itr; - } - - if (force) - { - for (int i = 0; i < m_sinks.size(); i++) - { - m_channelizers[i]->setChannelization(VORDEMOD_CHANNEL_SAMPLE_RATE, m_sinks[i]->m_frequencyOffset); - m_sinks[i]->applyChannelSettings(m_channelizers[i]->getChannelSampleRate(), m_channelizers[i]->getChannelFrequencyOffset()); - m_sinks[i]->applyAudioSampleRate(m_sinks[i]->getAudioSampleRate()); // reapply in case of channel sample rate change - } + m_channelizer->setChannelization(m_sink.getAudioSampleRate(), settings.m_inputFrequencyOffset); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_sink.applyAudioSampleRate(m_sink.getAudioSampleRate()); // reapply in case of channel sample rate change } if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); - for (int i = 0; i < m_sinks.size(); i++) - { - audioDeviceManager->removeAudioSink(m_sinks[i]->getAudioFifo()); - audioDeviceManager->addAudioSink(m_sinks[i]->getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); - int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); + //qDebug("AMDemod::applySettings: audioDeviceName: %s audioDeviceIndex: %d", qPrintable(settings.m_audioDeviceName), audioDeviceIndex); + audioDeviceManager->removeAudioSink(m_sink.getAudioFifo()); + audioDeviceManager->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); + int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); - if (m_sinks[i]->getAudioSampleRate() != audioSampleRate) - { - m_sinks[i]->applyAudioSampleRate(audioSampleRate); - } + if (m_sink.getAudioSampleRate() != audioSampleRate) + { + m_channelizer->setChannelization(audioSampleRate, settings.m_inputFrequencyOffset); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_sink.applyAudioSampleRate(audioSampleRate); } } - for (int i = 0; i < m_sinks.size(); i++) - m_sinks[i]->applySettings(settings, force); + m_sink.applySettings(settings, force); m_settings = settings; } - -void VORDemodSCBaseband::setBasebandSampleRate(int sampleRate) -{ - m_basebandSampleRate = sampleRate; - for (int i = 0; i < m_sinks.size(); i++) - { - m_channelizers[i]->setBasebandSampleRate(sampleRate); - calculateOffset(m_sinks[i]); - m_sinks[i]->applyChannelSettings(m_channelizers[i]->getChannelSampleRate(), m_channelizers[i]->getChannelFrequencyOffset()); - m_sinks[i]->applyAudioSampleRate(m_sinks[i]->getAudioSampleRate()); // reapply in case of channel sample rate change - } -} diff --git a/plugins/channelrx/demodvorsc/vordemodscbaseband.h b/plugins/channelrx/demodvorsc/vordemodscbaseband.h index 26db35f09..8c5d71109 100644 --- a/plugins/channelrx/demodvorsc/vordemodscbaseband.h +++ b/plugins/channelrx/demodvorsc/vordemodscbaseband.h @@ -64,68 +64,24 @@ public: void stopWork(); void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication - void getMagSqLevels(double& avg, double& peak, int& nbSamples) { - avg = 0.0; - peak = 0.0; - nbSamples = 0; - for (int i = 0; i < m_sinks.size(); i++) - { - double avg1, peak1; - int nbSamples1; - m_sinks[i]->getMagSqLevels(avg1, peak1, nbSamples1); - if (avg1 > avg) - { - avg = avg1; - nbSamples = nbSamples1; - } - avg += avg1; - if (peak1 > peak) - peak = peak1; - } - } - void setMessageQueueToGUI(MessageQueue *messageQueue) { - m_messageQueueToGUI = messageQueue; - for (int i = 0; i < m_sinks.size(); i++) - m_sinks[i]->setMessageQueueToGUI(messageQueue); - } - bool getSquelchOpen() const { - for (int i = 0; i < m_sinks.size(); i++) - { - if (m_sinks[i]->getSquelchOpen()) - return true; - } - return false; - } - int getAudioSampleRate() const { - if (m_sinks.size() > 0) - return m_sinks[0]->getAudioSampleRate(); - else - return 48000; - } - void setBasebandSampleRate(int sampleRate); - double getMagSq() const { - if (m_sinks.size() > 0) - return m_sinks[0]->getMagSq(); - else - return 0.0; - } + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { m_sink.getMagSqLevels(avg, peak, nbSamples); } + void setMessageQueueToGUI(MessageQueue *messageQueue) { m_sink.setMessageQueueToGUI(messageQueue); } + bool getSquelchOpen() const { return m_sink.getSquelchOpen(); } + int getAudioSampleRate() const { return m_sink.getAudioSampleRate(); } + double getMagSq() const { return m_sink.getMagSq(); } bool isRunning() const { return m_running; } private: SampleSinkFifo m_sampleFifo; - QList m_channelizers; - QList m_sinks; - AudioFifo m_audioFifoBug; // FIXME: Removing this results in audio stopping when demod is closed + DownChannelizer * m_channelizer; + VORDemodSCSink m_sink; MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication VORDemodSCSettings m_settings; + MessageQueue *m_messageQueueToGUI; bool m_running; QMutex m_mutex; - MessageQueue *m_messageQueueToGUI; - int m_basebandSampleRate; - int m_centerFrequency; bool handleMessage(const Message& cmd); - void calculateOffset(VORDemodSCSink *sink); void applySettings(const VORDemodSCSettings& settings, bool force = false); private slots: diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp index e1170ce54..da4b07301 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp @@ -17,22 +17,13 @@ /////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "vordemodscgui.h" +#include #include "device/deviceuiset.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" -#include "ui_vordemodscgui.h" +#include "dsp/dspengine.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" @@ -40,679 +31,15 @@ #include "util/units.h" #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" -#include "dsp/dspengine.h" #include "gui/crightclickenabler.h" #include "gui/audioselectdialog.h" #include "channel/channelwebapiutils.h" #include "maincore.h" +#include "ui_vordemodscgui.h" #include "vordemodsc.h" #include "vordemodscreport.h" -#include "vordemodscsink.h" - -#define VOR_COL_NAME 0 -#define VOR_COL_FREQUENCY 1 -#define VOR_COL_OFFSET 2 -#define VOR_COL_IDENT 3 -#define VOR_COL_MORSE 4 -#define VOR_COL_RX_IDENT 5 -#define VOR_COL_RX_MORSE 6 -#define VOR_COL_RADIAL 7 -#define VOR_COL_REF_MAG 8 -#define VOR_COL_VAR_MAG 9 -#define VOR_COL_MUTE 10 - -static const char *countryCodes[] = { - "ad", - "ae", - "af", - "ag", - "ai", - "al", - "am", - "an", - "ao", - "aq", - "ar", - "as", - "at", - "au", - "aw", - "ax", - "az", - "ba", - "bb", - "bd", - "be", - "bf", - "bg", - "bh", - "bi", - "bj", - "bl", - "bm", - "bn", - "bo", - "bq", - "br", - "bs", - "bt", - "bv", - "bw", - "by", - "bz", - "ca", - "cc", - "cd", - "cf", - "cg", - "ch", - "ci", - "ck", - "cl", - "cm", - "cn", - "co", - "cr", - "cu", - "cv", - "cw", - "cx", - "cy", - "cz", - "de", - "dj", - "dk", - "dm", - "do", - "dz", - "ec", - "ee", - "eg", - "eh", - "er", - "es", - "et", - "fi", - "fj", - "fk", - "fm", - "fo", - "fr", - "ga", - "gb", - "ge", - "gf", - "gg", - "gh", - "gi", - "gl", - "gm", - "gn", - "gp", - "gq", - "gr", - "gs", - "gt", - "gu", - "gw", - "gy", - "hk", - "hm", - "hn", - "hr", - "hu", - "id", - "ie", - "il", - "im", - "in", - "io", - "iq", - "ir", - "is", - "it", - "je", - "jm", - "jo", - "jp", - "ke", - "kg", - "kh", - "ki", - "km", - "kn", - "kp", - "kr", - "kw", - "ky", - "kz", - "la", - "lb", - "lc", - "li", - "lk", - "lr", - "ls", - "lt", - "lu", - "lv", - "ly", - "ma", - "mc", - "md", - "me", - "mf", - "mg", - "mh", - "mk", - "ml", - "mm", - "mn", - "mo", - "mp", - "mq", - "mr", - "ms", - "mt", - "mu", - "mv", - "mw", - "mx", - "my", - "mz", - "na", - "nc", - "ne", - "nf", - "ng", - "ni", - "nl", - "no", - "np", - "nr", - "nu", - "nz", - "om", - "pa", - "pe", - "pf", - "pg", - "ph", - "pk", - "pl", - "pm", - "pn", - "pr", - "ps", - "pt", - "pw", - "py", - "qa", - "re", - "ro", - "rs", - "ru", - "rw", - "sa", - "sb", - "sc", - "sd", - "se", - "sg", - "sh", - "si", - "sj", - "sk", - "sl", - "sm", - "sn", - "so", - "sr", - "ss", - "st", - "sv", - "sx", - "sy", - "sz", - "tc", - "td", - "tf", - "tg", - "th", - "tj", - "tk", - "tl", - "tm", - "tn", - "to", - "tr", - "tt", - "tv", - "tw", - "tz", - "ua", - "ug", - "um", - "us", - "uy", - "uz", - "va", - "vc", - "ve", - "vg", - "vi", - "vn", - "vu", - "wf", - "ws", - "ye", - "yt", - "za", - "zm", - "zw", - nullptr -}; - -// Lats and longs in decimal degrees. Distance in metres. Bearing in degrees. -// https://www.movable-type.co.uk/scripts/latlong.html -static void calcRadialEndPoint(float startLatitude, float startLongitude, float distance, float bearing, float &endLatitude, float &endLongitude) -{ - double startLatRad = startLatitude*M_PI/180.0; - double startLongRad = startLongitude*M_PI/180.0; - double theta = bearing*M_PI/180.0; - double earthRadius = 6378137.0; // At equator - double delta = distance/earthRadius; - double endLatRad = std::asin(sin(startLatRad)*cos(delta) + cos(startLatRad)*sin(delta)*cos(theta)); - double endLongRad = startLongRad + std::atan2(sin(theta)*sin(delta)*cos(startLatRad), cos(delta) - sin(startLatRad)*sin(endLatRad)); - endLatitude = endLatRad*180.0/M_PI; - endLongitude = endLongRad*180.0/M_PI; -} - -// Calculate intersection point along two radials -// https://www.movable-type.co.uk/scripts/latlong.html -static bool calcIntersectionPoint(float lat1, float lon1, float bearing1, float lat2, float lon2, float bearing2, float &intersectLat, float &intersectLon) -{ - - double lat1Rad = Units::degreesToRadians(lat1); - double lon1Rad = Units::degreesToRadians(lon1); - double lat2Rad = Units::degreesToRadians(lat2); - double lon2Rad = Units::degreesToRadians(lon2); - double theta13 = Units::degreesToRadians(bearing1); - double theta23 = Units::degreesToRadians(bearing2); - - double deltaLat = lat1Rad - lat2Rad; - double deltaLon = lon1Rad - lon2Rad; - double sindlat = sin(deltaLat/2.0); - double sindlon = sin(deltaLon/2.0); - double cosLat1 = cos(lat1Rad); - double cosLat2 = cos(lat2Rad); - double delta12 = 2.0 * asin(sqrt(sindlat*sindlat+cosLat1*cosLat2*sindlon*sindlon)); - if (abs(delta12) < std::numeric_limits::epsilon()) - return false; - - double sinLat1 = sin(lat1Rad); - double sinLat2 = sin(lat2Rad); - double sinDelta12 = sin(delta12); - double cosDelta12 = cos(delta12); - double thetaA = acos((sinLat2-sinLat1*cosDelta12)/(sinDelta12*cosLat1)); - double thetaB = acos((sinLat1-sinLat2*cosDelta12)/(sinDelta12*cosLat2)); - - double theta12, theta21; - if (sin(lon2Rad-lon1Rad) > 0.0) - { - theta12 = thetaA; - theta21 = 2.0*M_PI-thetaB; - } - else - { - theta12 = 2.0*M_PI-thetaA; - theta21 = thetaB; - } - double alpha1 = theta13 - theta12; - double alpha2 = theta21 - theta23; - double sinAlpha1 = sin(alpha1); - double sinAlpha2 = sin(alpha2); - if ((sinAlpha1 == 0.0) && (sinAlpha2 == 0.0)) - return false; - if (sinAlpha1*sinAlpha2 < 0.0) - return false; - double cosAlpha1 = cos(alpha1); - double cosAlpha2 = cos(alpha2); - double cosAlpha3 = -cosAlpha1*cosAlpha2+sinAlpha1*sinAlpha2*cos(delta12); - double delta13 = atan2(sin(delta12)*sinAlpha1*sinAlpha2, cosAlpha2+cosAlpha1*cosAlpha3); - double lat3Rad = asin(sinLat1*cos(delta13)+cosLat1*sin(delta13)*cos(theta13)); - double lon3Rad = lon1Rad + atan2(sin(theta13)*sin(delta13)*cosLat1, cos(delta13)-sinLat1*sin(lat3Rad)); - - intersectLat = Units::radiansToDegress(lat3Rad); - intersectLon = Units::radiansToDegress(lon3Rad); - - return true; -} - -VORGUI::VORGUI(NavAid *navAid, VORDemodSCGUI *gui) : - m_navAid(navAid), - m_gui(gui) -{ - // These are deleted by QTableWidget - m_nameItem = new QTableWidgetItem(); - m_frequencyItem = new QTableWidgetItem(); - m_offsetItem = new QTableWidgetItem(); - m_radialItem = new QTableWidgetItem(); - m_identItem = new QTableWidgetItem(); - m_morseItem = new QTableWidgetItem(); - m_rxIdentItem = new QTableWidgetItem(); - m_rxMorseItem = new QTableWidgetItem(); - m_varMagItem = new QTableWidgetItem(); - m_refMagItem = new QTableWidgetItem(); - - m_muteItem = new QWidget(); - - m_muteButton = new QToolButton(); - m_muteButton->setCheckable(true); - m_muteButton->setChecked(false); - m_muteButton->setToolTip("Mute/unmute audio from this VOR"); - m_muteButton->setIcon(m_gui->m_muteIcon); - - QHBoxLayout* pLayout = new QHBoxLayout(m_muteItem); - pLayout->addWidget(m_muteButton); - pLayout->setAlignment(Qt::AlignCenter); - pLayout->setContentsMargins(0, 0, 0, 0); - m_muteItem->setLayout(pLayout); - - connect(m_muteButton, &QPushButton::toggled, this, &VORGUI::on_audioMute_toggled); - - m_coordinates.push_back(QVariant::fromValue(*new QGeoCoordinate(m_navAid->m_latitude, m_navAid->m_longitude, Units::feetToMetres(m_navAid->m_elevation)))); -} - -void VORGUI::on_audioMute_toggled(bool checked) -{ - m_gui->m_settings.m_subChannelSettings.value(m_navAid->m_id)->m_audioMute = checked; - m_gui->applySettings(); -} - -QVariant VORModel::data(const QModelIndex &index, int role) const -{ - int row = index.row(); - if ((row < 0) || (row >= m_vors.count())) - return QVariant(); - if (role == VORModel::positionRole) - { - // Coordinates to display the VOR icon at - QGeoCoordinate coords; - coords.setLatitude(m_vors[row]->m_latitude); - coords.setLongitude(m_vors[row]->m_longitude); - coords.setAltitude(Units::feetToMetres(m_vors[row]->m_elevation)); - return QVariant::fromValue(coords); - } - else if (role == VORModel::vorDataRole) - { - // Create the text to go in the bubble next to the VOR - QStringList list; - list.append(QString("Name: %1").arg(m_vors[row]->m_name)); - list.append(QString("Frequency: %1 MHz").arg(m_vors[row]->m_frequencykHz / 1000.0f, 0, 'f', 1)); - if (m_vors[row]->m_channel != "") - list.append(QString("Channel: %1").arg(m_vors[row]->m_channel)); - list.append(QString("Ident: %1 %2").arg(m_vors[row]->m_ident).arg(Morse::toSpacedUnicodeMorse(m_vors[row]->m_ident))); - list.append(QString("Range: %1 nm").arg(m_vors[row]->m_range)); - if (m_vors[row]->m_alignedTrueNorth) - list.append(QString("Magnetic declination: Aligned to true North")); - else if (m_vors[row]->m_magneticDeclination != 0.0f) - list.append(QString("Magnetic declination: %1%2").arg(std::round(m_vors[row]->m_magneticDeclination)).arg(QChar(0x00b0))); - QString data = list.join("\n"); - return QVariant::fromValue(data); - } - else if (role == VORModel::vorImageRole) - { - // Select an image to use for the VOR - return QVariant::fromValue(QString("/demodvor/map/%1.png").arg(m_vors[row]->m_type)); - } - else if (role == VORModel::bubbleColourRole) - { - // Select a background colour for the text bubble next to the VOR - if (m_selected[row]) - return QVariant::fromValue(QColor("lightgreen")); - else - return QVariant::fromValue(QColor("lightblue")); - } - else if (role == VORModel::vorRadialRole) - { - // Draw a radial line from centre of VOR outwards at the demodulated angle - if (m_radialsVisible && m_selected[row] && (m_vorGUIs[row] != nullptr) && (m_radials[row] != -1.0f)) - { - QVariantList list; - - list.push_back(m_vorGUIs[row]->m_coordinates[0]); // Centre of VOR - - float endLat, endLong; - float bearing; - if (m_gui->m_settings.m_magDecAdjust && !m_vors[row]->m_alignedTrueNorth) - bearing = m_radials[row] - m_vors[row]->m_magneticDeclination; - else - bearing = m_radials[row]; - calcRadialEndPoint(m_vors[row]->m_latitude, m_vors[row]->m_longitude, m_vors[row]->getRangeMetres(), bearing, endLat, endLong); - list.push_back(QVariant::fromValue(*new QGeoCoordinate(endLat, endLong, Units::feetToMetres(m_vors[row]->m_elevation)))); - - return list; - } - else - return QVariantList(); - } - else if (role == VORModel::selectedRole) - return QVariant::fromValue(m_selected[row]); - return QVariant(); -} - -bool VORModel::setData(const QModelIndex &index, const QVariant& value, int role) -{ - int row = index.row(); - if ((row < 0) || (row >= m_vors.count())) - return false; - if (role == VORModel::selectedRole) - { - bool selected = value.toBool(); - VORGUI *vorGUI; - if (selected == true) - { - vorGUI = new VORGUI(m_vors[row], m_gui); - m_vorGUIs[row] = vorGUI; - } - else - vorGUI = m_vorGUIs[row]; - m_gui->selectVOR(vorGUI, selected); - m_selected[row] = selected; - emit dataChanged(index, index); - if (!selected) - { - delete vorGUI; - m_vorGUIs[row] = nullptr; - } - return true; - } - return true; -} - -// Find intersection between first two selected radials -bool VORModel::findIntersection(float &lat, float &lon) -{ - if (m_vors.count() > 2) - { - float lat1, lon1, bearing1, valid1 = false; - float lat2, lon2, bearing2, valid2 = false; - - for (int i = 0; i < m_vors.count(); i++) - { - if (m_selected[i] && (m_radials[i] >= 0.0)) - { - if (!valid1) - { - lat1 = m_vors[i]->m_latitude; - lon1 = m_vors[i]->m_longitude; - if (m_gui->m_settings.m_magDecAdjust && !m_vors[i]->m_alignedTrueNorth) - bearing1 = m_radials[i] - m_vors[i]->m_magneticDeclination; - else - bearing1 = m_radials[i]; - valid1 = true; - } - else - { - lat2 = m_vors[i]->m_latitude; - lon2 = m_vors[i]->m_longitude; - if (m_gui->m_settings.m_magDecAdjust && !m_vors[i]->m_alignedTrueNorth) - bearing2 = m_radials[i] - m_vors[i]->m_magneticDeclination; - else - bearing2 = m_radials[i]; - valid2 = true; - break; - } - } - } - - if (valid1 && valid2) - { - return calcIntersectionPoint(lat1, lon1, bearing1, lat2, lon2, bearing2, lat, lon); - } - } - - return false; -} - -void VORDemodSCGUI::resizeTable() -{ - // Fill table with a row of dummy data that will size the columns nicely - // Trailing spaces are for sort arrow - QString morse("---- ---- ----"); - int row = ui->vorData->rowCount(); - ui->vorData->setRowCount(row + 1); - ui->vorData->setItem(row, VOR_COL_NAME, new QTableWidgetItem("White Sulphur Springs")); - ui->vorData->setItem(row, VOR_COL_FREQUENCY, new QTableWidgetItem("Freq (MHz) ")); - ui->vorData->setItem(row, VOR_COL_OFFSET, new QTableWidgetItem("Offset (kHz) ")); - ui->vorData->setItem(row, VOR_COL_IDENT, new QTableWidgetItem("Ident ")); - ui->vorData->setItem(row, VOR_COL_MORSE, new QTableWidgetItem(Morse::toSpacedUnicode(morse))); - ui->vorData->setItem(row, VOR_COL_RADIAL, new QTableWidgetItem("Radial (o) ")); - ui->vorData->setItem(row, VOR_COL_RX_IDENT, new QTableWidgetItem("RX Ident ")); - ui->vorData->setItem(row, VOR_COL_RX_MORSE, new QTableWidgetItem(Morse::toSpacedUnicode(morse))); - ui->vorData->setItem(row, VOR_COL_VAR_MAG, new QTableWidgetItem("Var (dB) ")); - ui->vorData->setItem(row, VOR_COL_REF_MAG, new QTableWidgetItem("Ref (dB) ")); - ui->vorData->setItem(row, VOR_COL_MUTE, new QTableWidgetItem("Mute")); - ui->vorData->resizeColumnsToContents(); - ui->vorData->removeRow(row); -} - -// Columns in table reordered -void VORDemodSCGUI::vorData_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) -{ - (void) oldVisualIndex; - - m_settings.m_columnIndexes[logicalIndex] = newVisualIndex; -} - -// Column in table resized (when hidden size is 0) -void VORDemodSCGUI::vorData_sectionResized(int logicalIndex, int oldSize, int newSize) -{ - (void) oldSize; - - m_settings.m_columnSizes[logicalIndex] = newSize; -} - -// Right click in table header - show column select menu -void VORDemodSCGUI::columnSelectMenu(QPoint pos) -{ - menu->popup(ui->vorData->horizontalHeader()->viewport()->mapToGlobal(pos)); -} - -// Hide/show column when menu selected -void VORDemodSCGUI::columnSelectMenuChecked(bool checked) -{ - (void) checked; - - QAction* action = qobject_cast(sender()); - if (action != nullptr) - { - int idx = action->data().toInt(nullptr); - ui->vorData->setColumnHidden(idx, !action->isChecked()); - } -} - -// Create column select menu item -QAction *VORDemodSCGUI::createCheckableItem(QString &text, int idx, bool checked) -{ - QAction *action = new QAction(text, this); - action->setCheckable(true); - action->setChecked(checked); - action->setData(QVariant(idx)); - connect(action, SIGNAL(triggered()), this, SLOT(columnSelectMenuChecked())); - return action; -} - -// Called when a VOR is selected on the map -void VORDemodSCGUI::selectVOR(VORGUI *vorGUI, bool selected) -{ - if (selected) - { - m_selectedVORs.insert(vorGUI->m_navAid->m_id, vorGUI); - ui->vorData->setSortingEnabled(false); - int row = ui->vorData->rowCount(); - ui->vorData->setRowCount(row + 1); - ui->vorData->setItem(row, VOR_COL_NAME, vorGUI->m_nameItem); - ui->vorData->setItem(row, VOR_COL_FREQUENCY, vorGUI->m_frequencyItem); - ui->vorData->setItem(row, VOR_COL_OFFSET, vorGUI->m_offsetItem); - ui->vorData->setItem(row, VOR_COL_IDENT, vorGUI->m_identItem); - ui->vorData->setItem(row, VOR_COL_MORSE, vorGUI->m_morseItem); - ui->vorData->setItem(row, VOR_COL_RADIAL, vorGUI->m_radialItem); - ui->vorData->setItem(row, VOR_COL_RX_IDENT, vorGUI->m_rxIdentItem); - ui->vorData->setItem(row, VOR_COL_RX_MORSE, vorGUI->m_rxMorseItem); - ui->vorData->setItem(row, VOR_COL_VAR_MAG, vorGUI->m_varMagItem); - ui->vorData->setItem(row, VOR_COL_REF_MAG, vorGUI->m_refMagItem); - ui->vorData->setCellWidget(row, VOR_COL_MUTE, vorGUI->m_muteItem); - vorGUI->m_nameItem->setText(vorGUI->m_navAid->m_name); - vorGUI->m_identItem->setText(vorGUI->m_navAid->m_ident); - vorGUI->m_morseItem->setText(Morse::toSpacedUnicodeMorse(vorGUI->m_navAid->m_ident)); - vorGUI->m_frequencyItem->setData(Qt::DisplayRole, vorGUI->m_navAid->m_frequencykHz / 1000.0); - ui->vorData->setSortingEnabled(true); - - // Add to settings to create corresponding demodulator - VORDemodSubChannelSettings *subChannelSettings = new VORDemodSubChannelSettings(); - subChannelSettings->m_id = vorGUI->m_navAid->m_id; - subChannelSettings->m_frequency = vorGUI->m_navAid->m_frequencykHz * 1000; - subChannelSettings->m_audioMute = false; - m_settings.m_subChannelSettings.insert(vorGUI->m_navAid->m_id, subChannelSettings); - applySettings(); - } - else - { - m_selectedVORs.remove(vorGUI->m_navAid->m_id); - ui->vorData->removeRow(vorGUI->m_nameItem->row()); - // Remove from settings to remove corresponding demodulator - VORDemodSubChannelSettings *subChannelSettings = m_settings.m_subChannelSettings.value(vorGUI->m_navAid->m_id); - m_settings.m_subChannelSettings.remove(vorGUI->m_navAid->m_id); - delete subChannelSettings; - applySettings(); - } -} - -void VORDemodSCGUI::updateVORs() -{ - m_vorModel.removeAllVORs(); - QHash::iterator i = m_vors->begin(); - AzEl azEl = m_azEl; - - while (i != m_vors->end()) - { - NavAid *vor = i.value(); - - // Calculate distance to VOR from My Position - azEl.setTarget(vor->m_latitude, vor->m_longitude, Units::feetToMetres(vor->m_elevation)); - azEl.calculate(); - - // Only display VOR if in range - if (azEl.getDistance() <= 200000) - { - m_vorModel.addVOR(vor); - } - ++i; - } -} +#include "vordemodscgui.h" VORDemodSCGUI* VORDemodSCGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) { @@ -770,98 +97,62 @@ bool VORDemodSCGUI::handleMessage(const Message& message) else if (VORDemodSCReport::MsgReportRadial::match(message)) { VORDemodSCReport::MsgReportRadial& report = (VORDemodSCReport::MsgReportRadial&) message; - int subChannelId = report.getSubChannelId(); - - VORGUI *vorGUI = m_selectedVORs.value(subChannelId); - - // Display radial and signal magnitudes in table + // Display radial and signal magnitudes Real varMagDB = std::round(20.0*std::log10(report.getVarMag())); Real refMagDB = std::round(20.0*std::log10(report.getRefMag())); bool validRadial = (refMagDB > m_settings.m_refThresholdDB) && (varMagDB > m_settings.m_varThresholdDB); - vorGUI->m_radialItem->setData(Qt::DisplayRole, std::round(report.getRadial())); - if (validRadial) - vorGUI->m_radialItem->setForeground(QBrush(Qt::white)); - else - vorGUI->m_radialItem->setForeground(QBrush(Qt::red)); + ui->radialText->setText(tr("%1°").arg(std::round(report.getRadial()))); - vorGUI->m_refMagItem->setData(Qt::DisplayRole, refMagDB); - if (refMagDB > m_settings.m_refThresholdDB) - vorGUI->m_refMagItem->setForeground(QBrush(Qt::white)); - else - vorGUI->m_refMagItem->setForeground(QBrush(Qt::red)); + if (validRadial) { + ui->radialText->setStyleSheet("QLabel { color: white }"); + } else { + ui->radialText->setStyleSheet("QLabel { color: red }"); + } - vorGUI->m_varMagItem->setData(Qt::DisplayRole, varMagDB); - if (varMagDB > m_settings.m_varThresholdDB) - vorGUI->m_varMagItem->setForeground(QBrush(Qt::white)); - else - vorGUI->m_varMagItem->setForeground(QBrush(Qt::red)); + ui->refText->setText(tr("%1 dB").arg(refMagDB)); - // Update radial on map - m_vorModel.setRadial(subChannelId, validRadial, report.getRadial()); + if (refMagDB > m_settings.m_refThresholdDB) { + ui->refText->setStyleSheet("QLabel { color: white }"); + } else { + ui->refText->setStyleSheet("QLabel { color: red }"); + } + + ui->varText->setText(tr("%1 dB").arg(varMagDB)); + + if (varMagDB > m_settings.m_varThresholdDB) { + ui->varText->setStyleSheet("QLabel { color: white }"); + } else { + ui->varText->setStyleSheet("QLabel { color: red }"); + } return true; } - else if (VORDemodSCReport::MsgReportFreqOffset::match(message)) - { - VORDemodSCReport::MsgReportFreqOffset& report = (VORDemodSCReport::MsgReportFreqOffset&) message; - int subChannelId = report.getSubChannelId(); - - VORGUI *vorGUI = m_selectedVORs.value(subChannelId); - - vorGUI->m_offsetItem->setData(Qt::DisplayRole, report.getFreqOffset() / 1000.0); - if (report.getOutOfBand()) - { - vorGUI->m_offsetItem->setForeground(QBrush(Qt::red)); - // Clear other fields as data is now invalid - vorGUI->m_radialItem->setText(""); - vorGUI->m_refMagItem->setText(""); - vorGUI->m_varMagItem->setText(""); - m_vorModel.setRadial(subChannelId, false, -1.0f); - } - else - vorGUI->m_offsetItem->setForeground(QBrush(Qt::white)); - } else if (VORDemodSCReport::MsgReportIdent::match(message)) { VORDemodSCReport::MsgReportIdent& report = (VORDemodSCReport::MsgReportIdent&) message; - int subChannelId = report.getSubChannelId(); - - VORGUI *vorGUI = m_selectedVORs.value(subChannelId); QString ident = report.getIdent(); - // Convert Morse to a string - QString identString = Morse::toString(ident); + QString identString = Morse::toString(ident); // Convert Morse to a string + + ui->identText->setText(identString); + ui->morseText->setText(Morse::toSpacedUnicode(ident)); + // Idents should only be two or three characters, so filter anything else // other than TEST which indicates a VOR is under maintainance (may also be TST) if (((identString.size() >= 2) && (identString.size() <= 3)) || (identString == "TEST")) { - vorGUI->m_rxIdentItem->setText(identString); - vorGUI->m_rxMorseItem->setText(Morse::toSpacedUnicode(ident)); - if (vorGUI->m_navAid->m_ident == identString) - { - // Set colour to green if matching expected ident - vorGUI->m_rxIdentItem->setForeground(QBrush(Qt::green)); - vorGUI->m_rxMorseItem->setForeground(QBrush(Qt::green)); - } - else - { - // Set colour to green if not matching expected ident - vorGUI->m_rxIdentItem->setForeground(QBrush(Qt::red)); - vorGUI->m_rxMorseItem->setForeground(QBrush(Qt::red)); - } + ui->identText->setStyleSheet("QLabel { color: white }"); + ui->morseText->setStyleSheet("QLabel { color: white }"); } else { - // Set yellow to indicate we've filtered something (unless red) - if (vorGUI->m_rxIdentItem->foreground().color() != Qt::red) - { - vorGUI->m_rxIdentItem->setForeground(QBrush(Qt::yellow)); - vorGUI->m_rxMorseItem->setForeground(QBrush(Qt::yellow)); - } + ui->identText->setStyleSheet("QLabel { color: yellow }"); + ui->morseText->setStyleSheet("QLabel { color: yellow }"); } + return true; } @@ -883,6 +174,9 @@ void VORDemodSCGUI::handleInputMessages() void VORDemodSCGUI::channelMarkerChangedByCursor() { + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + applySettings(); } void VORDemodSCGUI::channelMarkerHighlightedByCursor() @@ -890,6 +184,13 @@ void VORDemodSCGUI::channelMarkerHighlightedByCursor() setHighlighted(m_channelMarker.getHighlighted()); } +void VORDemodSCGUI::on_deltaFrequency_changed(qint64 value) +{ + m_channelMarker.setCenterFrequency(value); + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + applySettings(); +} + void VORDemodSCGUI::on_thresh_valueChanged(int value) { ui->threshText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1)); @@ -917,188 +218,6 @@ void VORDemodSCGUI::on_audioMute_toggled(bool checked) applySettings(); } -qint64 VORDemodSCGUI::fileAgeInDays(QString filename) -{ - QFile file(filename); - if (file.exists()) - { - QDateTime modified = file.fileTime(QFileDevice::FileModificationTime); - if (modified.isValid()) - return modified.daysTo(QDateTime::currentDateTime()); - else - return -1; - } - return -1; -} - -bool VORDemodSCGUI::confirmDownload(QString filename) -{ - qint64 age = fileAgeInDays(filename); - if ((age == -1) || (age > 100)) - return true; - else - { - QMessageBox::StandardButton reply; - if (age == 0) - reply = QMessageBox::question(this, "Confirm download", "This file was last downloaded today. Are you sure you wish to redownload it?", QMessageBox::Yes|QMessageBox::No); - else if (age == 1) - reply = QMessageBox::question(this, "Confirm download", "This file was last downloaded yesterday. Are you sure you wish to redownload it?", QMessageBox::Yes|QMessageBox::No); - else - reply = QMessageBox::question(this, "Confirm download", QString("This file was last downloaded %1 days ago. Are you sure you wish to redownload this file?").arg(age), QMessageBox::Yes|QMessageBox::No); - return reply == QMessageBox::Yes; - } -} - -QString VORDemodSCGUI::getDataDir() -{ - // Get directory to store app data in - QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); - // First dir is writable - return locations[0]; -} - -QString VORDemodSCGUI::getOpenAIPVORDBFilename(int i) -{ - if (countryCodes[i] != nullptr) - return getDataDir() + "/" + countryCodes[i] + "_nav.aip"; - else - return ""; -} - -QString VORDemodSCGUI::getOpenAIPVORDBURL(int i) -{ - if (countryCodes[i] != nullptr) - return QString(OPENAIP_NAVAIDS_URL).arg(countryCodes[i]); - else - return ""; -} - -QString VORDemodSCGUI::getVORDBFilename() -{ - return getDataDir() + "/vorDatabase.csv"; -} - -void VORDemodSCGUI::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes) -{ - m_progressDialog->setMaximum(totalBytes); - m_progressDialog->setValue(bytesRead); -} - -void VORDemodSCGUI::downloadFinished(const QString& filename, bool success) -{ - if (success) - { - if (filename == getVORDBFilename()) - { - m_vors = NavAid::readNavAidsDB(filename); - if (m_vors != nullptr) - updateVORs(); - m_progressDialog->close(); - m_progressDialog = nullptr; - } - else if (filename == getOpenAIPVORDBFilename(m_countryIndex)) - { - m_countryIndex++; - if (countryCodes[m_countryIndex] != nullptr) - { - QString vorDBFile = getOpenAIPVORDBFilename(m_countryIndex); - QString urlString = getOpenAIPVORDBURL(m_countryIndex); - QUrl dbURL(urlString); - m_progressDialog->setLabelText(QString("Downloading %1.").arg(urlString)); - m_progressDialog->setValue(m_countryIndex); - m_dlm.download(dbURL, vorDBFile); - } - else - { - readNavAids(); - if (m_vors != nullptr) - updateVORs(); - m_progressDialog->close(); - m_progressDialog = nullptr; - } - } - else - { - qDebug() << "VORDemodSCGUI::downloadFinished: Unexpected filename: " << filename; - m_progressDialog->close(); - m_progressDialog = nullptr; - } - } - else - { - qDebug() << "VORDemodSCGUI::downloadFinished: Failed: " << filename; - m_progressDialog->close(); - m_progressDialog = nullptr; - QMessageBox::warning(this, "Download failed", QString("Failed to download %1").arg(filename)); - } -} - -void VORDemodSCGUI::on_getOurAirportsVORDB_clicked(bool checked) -{ - (void) checked; - - // Don't try to download while already in progress - if (m_progressDialog == nullptr) - { - QString vorDBFile = getVORDBFilename(); - if (confirmDownload(vorDBFile)) - { - // Download OurAirports navaid database to disk - QUrl dbURL(QString(OURAIRPORTS_NAVAIDS_URL)); - m_progressDialog = new QProgressDialog(this); - m_progressDialog->setAttribute(Qt::WA_DeleteOnClose); - m_progressDialog->setCancelButton(nullptr); - m_progressDialog->setMinimumDuration(500); - m_progressDialog->setLabelText(QString("Downloading %1.").arg(OURAIRPORTS_NAVAIDS_URL)); - QNetworkReply *reply = m_dlm.download(dbURL, vorDBFile); - connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateDownloadProgress(qint64,qint64))); - } - } -} - -void VORDemodSCGUI::on_getOpenAIPVORDB_clicked(bool checked) -{ - (void) checked; - - // Don't try to download while already in progress - if (m_progressDialog == nullptr) - { - m_countryIndex = 0; - QString vorDBFile = getOpenAIPVORDBFilename(m_countryIndex); - if (confirmDownload(vorDBFile)) - { - // Download OpenAIP XML to disk - QString urlString = getOpenAIPVORDBURL(m_countryIndex); - QUrl dbURL(urlString); - m_progressDialog = new QProgressDialog(this); - m_progressDialog->setAttribute(Qt::WA_DeleteOnClose); - m_progressDialog->setCancelButton(nullptr); - m_progressDialog->setMinimumDuration(500); - m_progressDialog->setMaximum(sizeof(countryCodes)/sizeof(countryCodes[0])); - m_progressDialog->setValue(0); - m_progressDialog->setLabelText(QString("Downloading %1.").arg(urlString)); - m_dlm.download(dbURL, vorDBFile); - } - } -} - -void VORDemodSCGUI::readNavAids() -{ - m_vors = new QHash(); - for (int countryIndex = 0; countryCodes[countryIndex] != nullptr; countryIndex++) - { - QString vorDBFile = getOpenAIPVORDBFilename(countryIndex); - NavAid::readNavAidsXML(m_vors, vorDBFile); - } -} - -void VORDemodSCGUI::on_magDecAdjust_clicked(bool checked) -{ - m_settings.m_magDecAdjust = checked; - m_vorModel.allVORUpdated(); - applySettings(); -} - void VORDemodSCGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; @@ -1157,23 +276,13 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_channelMarker(this), m_doApplySettings(true), m_squelchOpen(false), - m_tickCount(0), - m_progressDialog(nullptr), - m_vorModel(this), - m_vors(nullptr) + m_tickCount(0) { ui->setupUi(this); - ui->map->rootContext()->setContextProperty("vorModel", &m_vorModel); - ui->map->setSource(QUrl(QStringLiteral("qrc:/demodvor/map/map.qml"))); - - m_muteIcon.addPixmap(QPixmap("://sound_off.png"), QIcon::Normal, QIcon::On); - m_muteIcon.addPixmap(QPixmap("://sound_on.png"), QIcon::Normal, QIcon::Off); - setAttribute(Qt::WA_DeleteOnClose, true); connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); - connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &VORDemodSCGUI::downloadFinished); m_vorDemod = reinterpret_cast(rxChannel); m_vorDemod->setMessageQueueToGUI(getInputMessageQueue()); @@ -1183,12 +292,15 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute); connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); + ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); + ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999); ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue); - m_channelMarker.blockSignals(true); - m_channelMarker.setColor(Qt::yellow); - m_channelMarker.setBandwidth(2*48000); - m_channelMarker.setCenterFrequency(0); + m_channelMarker.blockSignals(true); + m_channelMarker.setColor(Qt::yellow); + m_channelMarker.setBandwidth(2*VORDemodSCSettings::VORDEMOD_CHANNEL_BANDWIDTH); + m_channelMarker.setCenterFrequency(0); m_channelMarker.setTitle("VOR Demodulator"); m_channelMarker.blockSignals(false); m_channelMarker.setVisible(true); // activate signal on the last setting only @@ -1203,68 +315,6 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); - // Get station position - Real stationLatitude = MainCore::instance()->getSettings().getLatitude(); - Real stationLongitude = MainCore::instance()->getSettings().getLongitude(); - Real stationAltitude = MainCore::instance()->getSettings().getAltitude(); - m_azEl.setLocation(stationLatitude, stationLongitude, stationAltitude); - - // Centre map at My Position - QQuickItem *item = ui->map->rootObject(); - QObject *object = item->findChild("map"); - if(object != NULL) - { - QGeoCoordinate coords = object->property("center").value(); - coords.setLatitude(stationLatitude); - coords.setLongitude(stationLongitude); - object->setProperty("center", QVariant::fromValue(coords)); - } - // Move antenna icon to My Position to start with - QObject *stationObject = item->findChild("station"); - if(stationObject != NULL) - { - QGeoCoordinate coords = stationObject->property("coordinate").value(); - coords.setLatitude(stationLatitude); - coords.setLongitude(stationLongitude); - coords.setAltitude(stationAltitude); - stationObject->setProperty("coordinate", QVariant::fromValue(coords)); - stationObject->setProperty("stationName", QVariant::fromValue(MainCore::instance()->getSettings().getStationName())); - } - - // Read in VOR information if it exists - bool useOurAirports = false; - if (useOurAirports) - { - m_vors = NavAid::readNavAidsDB(getVORDBFilename()); - ui->getOpenAIPVORDB->setVisible(false); - } - else - { - readNavAids(); - ui->getOurAirportsVORDB->setVisible(false); - } - if (m_vors != nullptr) - updateVORs(); - - // Resize the table using dummy data - resizeTable(); - // Allow user to reorder columns - ui->vorData->horizontalHeader()->setSectionsMovable(true); - // Allow user to sort table by clicking on headers - ui->vorData->setSortingEnabled(true); - // Add context menu to allow hiding/showing of columns - menu = new QMenu(ui->vorData); - for (int i = 0; i < ui->vorData->horizontalHeader()->count(); i++) - { - QString text = ui->vorData->horizontalHeaderItem(i)->text(); - menu->addAction(createCheckableItem(text, i, true)); - } - ui->vorData->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->vorData->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(columnSelectMenu(QPoint))); - // Get signals when columns change - connect(ui->vorData->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(vorData_sectionMoved(int, int, int))); - connect(ui->vorData->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(vorData_sectionResized(int, int, int))); - displaySettings(); applySettings(true); } @@ -1291,8 +341,8 @@ void VORDemodSCGUI::applySettings(bool force) void VORDemodSCGUI::displaySettings() { m_channelMarker.blockSignals(true); - m_channelMarker.setCenterFrequency(0); - m_channelMarker.setBandwidth(m_basebandSampleRate > 0 ? m_basebandSampleRate : 2*48000); + m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); + m_channelMarker.setBandwidth(2*VORDemodSCSettings::VORDEMOD_CHANNEL_BANDWIDTH); m_channelMarker.setTitle(m_settings.m_title); m_channelMarker.blockSignals(false); m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only @@ -1302,6 +352,8 @@ void VORDemodSCGUI::displaySettings() blockApplySettings(true); + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + ui->thresh->setValue(m_settings.m_identThreshold * 10.0); ui->threshText->setText(QString("%1").arg(m_settings.m_identThreshold, 0, 'f', 1)); @@ -1315,18 +367,6 @@ void VORDemodSCGUI::displaySettings() displayStreamIndex(); - // Order and size columns - QHeaderView *header = ui->vorData->horizontalHeader(); - for (int i = 0; i < VORDEMOD_COLUMNS; i++) - { - bool hidden = m_settings.m_columnSizes[i] == 0; - header->setSectionHidden(i, hidden); - menu->actions().at(i)->setChecked(!hidden); - if (m_settings.m_columnSizes[i] > 0) - ui->vorData->setColumnWidth(i, m_settings.m_columnSizes[i]); - header->moveSection(header->visualIndex(i), m_settings.m_columnIndexes[i]); - } - blockApplySettings(false); } @@ -1395,26 +435,5 @@ void VORDemodSCGUI::tick() m_squelchOpen = squelchOpen; } - // Try to determine position, based on intersection of two radials - if (m_tickCount % 50) - { - float lat, lon; - - if (m_vorModel.findIntersection(lat, lon)) - { - // Move antenna icon to estimated position - QQuickItem *item = ui->map->rootObject(); - QObject *stationObject = item->findChild("station"); - if(stationObject != NULL) - { - QGeoCoordinate coords = stationObject->property("coordinate").value(); - coords.setLatitude(lat); - coords.setLongitude(lon); - stationObject->setProperty("coordinate", QVariant::fromValue(coords)); - stationObject->setProperty("stationName", QVariant::fromValue(MainCore::instance()->getSettings().getStationName())); - } - } - } - m_tickCount++; } diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.h b/plugins/channelrx/demodvorsc/vordemodscgui.h index eb1176587..b72010a52 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.h +++ b/plugins/channelrx/demodvorsc/vordemodscgui.h @@ -19,26 +19,11 @@ #ifndef INCLUDE_VORDEMODSCGUI_H #define INCLUDE_VORDEMODSCGUI_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "channel/channelgui.h" #include "dsp/channelmarker.h" #include "dsp/movingaverage.h" #include "util/messagequeue.h" -#include "util/httpdownloadmanager.h" -#include "util/azel.h" #include "vordemodscsettings.h" -#include "navaid.h" class PluginAPI; class DeviceUISet; @@ -51,151 +36,6 @@ namespace Ui { } class VORDemodSCGUI; -// Table items for each VOR -class VORGUI : public QObject { - Q_OBJECT -public: - NavAid *m_navAid; - QVariantList m_coordinates; - VORDemodSCGUI *m_gui; - - QTableWidgetItem *m_nameItem; - QTableWidgetItem *m_frequencyItem; - QTableWidgetItem *m_offsetItem; - QTableWidgetItem *m_identItem; - QTableWidgetItem *m_morseItem; - QTableWidgetItem *m_radialItem; - QTableWidgetItem *m_rxIdentItem; - QTableWidgetItem *m_rxMorseItem; - QTableWidgetItem *m_varMagItem; - QTableWidgetItem *m_refMagItem; - QWidget *m_muteItem; - QToolButton *m_muteButton; - - VORGUI(NavAid *navAid, VORDemodSCGUI *gui); -private slots: - void on_audioMute_toggled(bool checked); -}; - -// VOR model used for each VOR on the map -class VORModel : public QAbstractListModel { - Q_OBJECT - -public: - using QAbstractListModel::QAbstractListModel; - enum MarkerRoles{ - positionRole = Qt::UserRole + 1, - vorDataRole = Qt::UserRole + 2, - vorImageRole = Qt::UserRole + 3, - vorRadialRole = Qt::UserRole + 4, - bubbleColourRole = Qt::UserRole + 5, - selectedRole = Qt::UserRole + 6 - }; - - VORModel(VORDemodSCGUI *gui) : - m_gui(gui), - m_radialsVisible(true) - { - } - - Q_INVOKABLE void addVOR(NavAid *vor) { - beginInsertRows(QModelIndex(), rowCount(), rowCount()); - m_vors.append(vor); - m_selected.append(false); - m_radials.append(-1.0f); - m_vorGUIs.append(nullptr); - endInsertRows(); - } - - int rowCount(const QModelIndex &parent = QModelIndex()) const override { - Q_UNUSED(parent) - return m_vors.count(); - } - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - bool setData(const QModelIndex &index, const QVariant& value, int role = Qt::EditRole) override; - - Qt::ItemFlags flags(const QModelIndex &index) const override { - (void) index; - return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; - } - - void allVORUpdated() { - for (int i = 0; i < m_vors.count(); i++) - { - QModelIndex idx = index(i); - emit dataChanged(idx, idx); - } - } - - void removeVOR(NavAid *vor) { - int row = m_vors.indexOf(vor); - if (row >= 0) - { - beginRemoveRows(QModelIndex(), row, row); - m_vors.removeAt(row); - m_selected.removeAt(row); - m_radials.removeAt(row); - m_vorGUIs.removeAt(row); - endRemoveRows(); - } - } - - void removeAllVORs() { - beginRemoveRows(QModelIndex(), 0, m_vors.count()); - m_vors.clear(); - m_selected.clear(); - m_radials.clear(); - m_vorGUIs.clear(); - endRemoveRows(); - } - - QHash roleNames() const { - QHash roles; - roles[positionRole] = "position"; - roles[vorDataRole] = "vorData"; - roles[vorImageRole] = "vorImage"; - roles[vorRadialRole] = "vorRadial"; - roles[bubbleColourRole] = "bubbleColour"; - roles[selectedRole] = "selected"; - return roles; - } - - void setRadialsVisible(bool radialsVisible) - { - m_radialsVisible = radialsVisible; - allVORUpdated(); - } - - void setRadial(int id, bool valid, Real radial) - { - for (int i = 0; i < m_vors.count(); i++) - { - if (m_vors[i]->m_id == id) - { - if (valid) - m_radials[i] = radial; - else - m_radials[i] = -1; // -1 to indicate invalid - QModelIndex idx = index(i); - emit dataChanged(idx, idx); - break; - } - } - } - - bool findIntersection(float &lat, float &lon); - -private: - VORDemodSCGUI *m_gui; - bool m_radialsVisible; - QList m_vors; - QList m_selected; - QList m_radials; - QList m_vorGUIs; -}; - class VORDemodSCGUI : public ChannelGUI { Q_OBJECT @@ -207,16 +47,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - void selectVOR(VORGUI *vorGUI, bool selected); public slots: void channelMarkerChangedByCursor(); void channelMarkerHighlightedByCursor(); private: - friend class VORGUI; - friend class VORModel; - Ui::VORDemodSCGUI* ui; PluginAPI* m_pluginAPI; DeviceUISet* m_deviceUISet; @@ -230,16 +66,6 @@ private: uint32_t m_tickCount; MessageQueue m_inputMessageQueue; - QMenu *menu; // Column select context menu - HttpDownloadManager m_dlm; - QProgressDialog *m_progressDialog; - int m_countryIndex; - VORModel m_vorModel; - QHash *m_vors; - QHash m_selectedVORs; - AzEl m_azEl; // Position of station - QIcon m_muteIcon; - explicit VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0); virtual ~VORDemodSCGUI(); @@ -252,37 +78,14 @@ private: void leaveEvent(QEvent*); void enterEvent(QEvent*); - void resizeTable(); - QAction *createCheckableItem(QString& text, int idx, bool checked); - - void calculateFreqOffset(VORGUI *vorGUI); - void calculateFreqOffsets(); - void updateVORs(); - QString getOpenAIPVORDBURL(int i); - QString getOpenAIPVORDBFilename(int i); - QString getVORDBFilename(); - void readNavAids(); - // Move to util - QString getDataDir(); - qint64 fileAgeInDays(QString filename); - bool confirmDownload(QString filename); - private slots: + void on_deltaFrequency_changed(qint64 value); void on_thresh_valueChanged(int value); void on_volume_valueChanged(int value); void on_squelch_valueChanged(int value); void on_audioMute_toggled(bool checked); - void on_getOurAirportsVORDB_clicked(bool checked = false); - void on_getOpenAIPVORDB_clicked(bool checked = false); - void on_magDecAdjust_clicked(bool checked = false); - void vorData_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); - void vorData_sectionResized(int logicalIndex, int oldSize, int newSize); - void columnSelectMenu(QPoint pos); - void columnSelectMenuChecked(bool checked = false); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); - void updateDownloadProgress(qint64 bytesRead, qint64 totalBytes); - void downloadFinished(const QString& filename, bool success); void handleInputMessages(); void audioSelect(); void tick(); diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.ui b/plugins/channelrx/demodvorsc/vordemodscgui.ui index d90cdcb45..963daf35e 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.ui +++ b/plugins/channelrx/demodvorsc/vordemodscgui.ui @@ -6,8 +6,8 @@ 0 0 - 398 - 893 + 402 + 189 @@ -19,7 +19,7 @@ 352 - 0 + 110 @@ -43,7 +43,7 @@ 0 0 390 - 61 + 131 @@ -71,6 +71,105 @@ 2 + + + + 2 + + + + + + + + 16 + 0 + + + + Df + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Demod shift frequency from center in Hz + + + + + + + Hz + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Channel power + + + Qt::RightToLeft + + + 0.0 + + + + + + + dB + + + + + + + @@ -123,6 +222,12 @@ + + + 40 + 0 + + Channel power @@ -130,7 +235,10 @@ Qt::RightToLeft - 0.0 + -100.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -165,70 +273,6 @@ - - - - true - - - Download OurAirports VOR database - - - - - - - :/demodvor/icons/vor.png:/demodvor/icons/vor.png - - - - - - - Download OpenAIP VOR database - - - - - - - :/demodvor/icons/vor.png:/demodvor/icons/vor.png - - - - - - - Draw radials adjusted for magnetic declination - - - - - - - :/demodvor/icons/compass.png:/demodvor/icons/compass.png - - - true - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -287,11 +331,17 @@ - + - Qt::Vertical + Qt::Horizontal - + + + 40 + 20 + + + @@ -386,209 +436,201 @@ - - - - - - 0 - 110 - 391 - 140 - - - - - 0 - 0 - - - - VORs - - - - 2 - - - 3 - - - 3 - - - 3 - - - 3 - - - - QAbstractItemView::NoEditTriggers - - - - Name - - - Name of the VOR - - - - - Freq (MHz) - - - Frequency of the VOR in MHz - - - - - Offset (kHz) - - - Offset of the VOR's frequency from the current center frequency. Red indicates out of range. - - - - - Ident - - - Ident for the VOR - - - - - Morse - - - Morse code ident for the VOR - - - - - RX Ident - - - Received ident - - - - - RX Morse - - - Received Morse code ident - - - - - Radial (°) - - - Calculated radial from the VOR - - - - - Ref (dB) - - - Magnitude of received reference signal in dB - - - - - Var (dB) - - - Magnitude of received variable signal in dB - - - - - Mute - - - Mute/unmute audio from selected VORs - - - + + + + + + 20 + 0 + + + + Rad + + + + + + + + 0 + 0 + + + + + 30 + 0 + + + + Radial direction (degrees) red if low confidence + + + 359d + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + Ref + + + + + + + + 50 + 0 + + + + Magnitude of the received 30Hz FM reference signal in dB + + + -100 dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + Var + + + + + + + + 50 + 0 + + + + Magnitude of the received 30Hz AM variable signal in dB + + + -100 dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - - - - 0 - 260 - 391 - 581 - - - - - 0 - 0 - - - - Map - - - - 2 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - 0 - 0 - - - - - 100 - 500 - - - - VOR map - - - QQuickWidget::SizeRootObjectToView - - - - - - - + + + + + Ident + + + + + + + + 50 + 0 + + + + Decoded identity code yellow if not matching rules + + + XXX + + + + + + + Qt::Vertical + + + + + + + Morse + + + + + + + + 120 + 0 + + + + Morse identity decoded yellow if not matching rules + + + --- --- --- + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - QQuickWidget - QWidget -
QtQuickWidgets/QQuickWidget
-
RollupWidget QWidget @@ -601,12 +643,15 @@
gui/levelmeter.h
1
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
audioMute - getOurAirportsVORDB - vorData - map diff --git a/plugins/channelrx/demodvorsc/vordemodscreport.cpp b/plugins/channelrx/demodvorsc/vordemodscreport.cpp index d0405b015..1bc6842c8 100644 --- a/plugins/channelrx/demodvorsc/vordemodscreport.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscreport.cpp @@ -17,6 +17,5 @@ #include "vordemodscreport.h" -MESSAGE_CLASS_DEFINITION(VORDemodSCReport::MsgReportFreqOffset, Message) MESSAGE_CLASS_DEFINITION(VORDemodSCReport::MsgReportRadial, Message) MESSAGE_CLASS_DEFINITION(VORDemodSCReport::MsgReportIdent, Message) diff --git a/plugins/channelrx/demodvorsc/vordemodscreport.h b/plugins/channelrx/demodvorsc/vordemodscreport.h index fde40c276..1da87bf76 100644 --- a/plugins/channelrx/demodvorsc/vordemodscreport.h +++ b/plugins/channelrx/demodvorsc/vordemodscreport.h @@ -31,25 +31,21 @@ public: MESSAGE_CLASS_DECLARATION public: - int getSubChannelId() const { return m_subChannelId; } float getRadial() const { return m_radial; } float getRefMag() const { return m_refMag; } float getVarMag() const { return m_varMag; } - static MsgReportRadial* create(int subChannelId, float radial, float refMag, float varMag) - { - return new MsgReportRadial(subChannelId, radial, refMag, varMag); + static MsgReportRadial* create(float radial, float refMag, float varMag) { + return new MsgReportRadial(radial, refMag, varMag); } private: - int m_subChannelId; float m_radial; float m_refMag; float m_varMag; - MsgReportRadial(int subChannelId, float radial, float refMag, float varMag) : + MsgReportRadial(float radial, float refMag, float varMag) : Message(), - m_subChannelId(subChannelId), m_radial(radial), m_refMag(refMag), m_varMag(varMag) @@ -57,52 +53,22 @@ public: } }; - class MsgReportFreqOffset : public Message { - MESSAGE_CLASS_DECLARATION - - public: - int getSubChannelId() const { return m_subChannelId; } - int getFreqOffset() const { return m_freqOffset; } - bool getOutOfBand() const { return m_outOfBand; } - - static MsgReportFreqOffset* create(int subChannelId, int freqOffset, bool outOfBand) - { - return new MsgReportFreqOffset(subChannelId, freqOffset, outOfBand); - } - - private: - int m_subChannelId; - int m_freqOffset; - bool m_outOfBand; - - MsgReportFreqOffset(int subChannelId, int freqOffset, bool outOfBand) : - Message(), - m_subChannelId(subChannelId), - m_freqOffset(freqOffset), - m_outOfBand(outOfBand) - { - } - }; - class MsgReportIdent : public Message { MESSAGE_CLASS_DECLARATION public: - int getSubChannelId() const { return m_subChannelId; } QString getIdent() const { return m_ident; } - static MsgReportIdent* create(int subChannelId, QString ident) + static MsgReportIdent* create(QString ident) { - return new MsgReportIdent(subChannelId, ident); + return new MsgReportIdent(ident); } private: - int m_subChannelId; QString m_ident; - MsgReportIdent(int subChannelId, QString ident) : + MsgReportIdent(QString ident) : Message(), - m_subChannelId(subChannelId), m_ident(ident) { } diff --git a/plugins/channelrx/demodvorsc/vordemodscsettings.cpp b/plugins/channelrx/demodvorsc/vordemodscsettings.cpp index 3e8766d25..36d54e437 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsettings.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscsettings.cpp @@ -31,6 +31,7 @@ VORDemodSCSettings::VORDemodSCSettings() : void VORDemodSCSettings::resetToDefaults() { + m_inputFrequencyOffset = 0; m_squelch = -60.0; m_volume = 2.0; m_audioMute = false; @@ -47,18 +48,12 @@ void VORDemodSCSettings::resetToDefaults() m_identThreshold = 2.0; m_refThresholdDB = -45.0; m_varThresholdDB = -90.0; - m_magDecAdjust = true; - - for (int i = 0; i < VORDEMOD_COLUMNS; i++) - { - m_columnIndexes[i] = i; - m_columnSizes[i] = -1; // Autosize - } } QByteArray VORDemodSCSettings::serialize() const { SimpleSerializer s(1); + s.writeS32(1, m_inputFrequencyOffset); s.writeS32(3, m_streamIndex); s.writeS32(4, m_volume*10); s.writeS32(5, m_squelch); @@ -79,12 +74,6 @@ QByteArray VORDemodSCSettings::serialize() const s.writeReal(20, m_identThreshold); s.writeReal(21, m_refThresholdDB); s.writeReal(22, m_varThresholdDB); - s.writeBool(23, m_magDecAdjust); - - for (int i = 0; i < VORDEMOD_COLUMNS; i++) - s.writeS32(100 + i, m_columnIndexes[i]); - for (int i = 0; i < VORDEMOD_COLUMNS; i++) - s.writeS32(200 + i, m_columnSizes[i]); return s.final(); } @@ -106,6 +95,7 @@ bool VORDemodSCSettings::deserialize(const QByteArray& data) uint32_t utmp; QString strtmp; + d.readS32(1, &m_inputFrequencyOffset, 0); d.readS32(3, &m_streamIndex, 0); d.readS32(4, &tmp, 20); m_volume = tmp * 0.1; @@ -138,12 +128,6 @@ bool VORDemodSCSettings::deserialize(const QByteArray& data) d.readReal(20, &m_identThreshold, 2.0); d.readReal(21, &m_refThresholdDB, -45.0); d.readReal(22, &m_varThresholdDB, -90.0); - d.readBool(23, &m_magDecAdjust, true); - - for (int i = 0; i < VORDEMOD_COLUMNS; i++) - d.readS32(100 + i, &m_columnIndexes[i], i); - for (int i = 0; i < VORDEMOD_COLUMNS; i++) - d.readS32(200 + i, &m_columnSizes[i], -1); return true; } diff --git a/plugins/channelrx/demodvorsc/vordemodscsettings.h b/plugins/channelrx/demodvorsc/vordemodscsettings.h index 00fb0a107..befba9997 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsettings.h +++ b/plugins/channelrx/demodvorsc/vordemodscsettings.h @@ -24,17 +24,9 @@ class Serializable; -// Number of columns in the table -#define VORDEMOD_COLUMNS 11 - -struct VORDemodSubChannelSettings { - int m_id; //!< Unique VOR identifier (from database) - int m_frequency; //!< Frequency the VOR is on - bool m_audioMute; //!< Mute the audio from this VOR -}; - struct VORDemodSCSettings { + qint32 m_inputFrequencyOffset; Real m_squelch; Real m_volume; bool m_audioMute; @@ -52,12 +44,14 @@ struct VORDemodSCSettings Real m_identThreshold; //!< Linear SNR threshold for Morse demodulator Real m_refThresholdDB; //!< Threshold in dB for valid VOR reference signal Real m_varThresholdDB; //!< Threshold in dB for valid VOR variable signal - bool m_magDecAdjust; //!< Adjust for magnetic declination when drawing radials on the map - int m_columnIndexes[VORDEMOD_COLUMNS];//!< How the columns are ordered in the table - int m_columnSizes[VORDEMOD_COLUMNS]; //!< Size of the coumns in the table - - QHash m_subChannelSettings; + // Highest frequency is the FM subcarrier at up to ~11kHz + // However, old VORs can have 0.005% frequency offset, which is 6kHz + static const int VORDEMOD_CHANNEL_BANDWIDTH = 18000; + // Sample rate needs to be at least twice the above + // Also need to consider impact frequency resolution of Goertzel filters + // May as well make it a common audio rate, to possibly avoid decimation + static const int VORDEMOD_CHANNEL_SAMPLE_RATE = 48000; VORDemodSCSettings(); void resetToDefaults(); diff --git a/plugins/channelrx/demodvorsc/vordemodscsink.cpp b/plugins/channelrx/demodvorsc/vordemodscsink.cpp index dce9f9492..fb565579f 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsink.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscsink.cpp @@ -27,14 +27,13 @@ #include "util/morse.h" #include "util/units.h" -#include "vordemodscsink.h" #include "vordemodscreport.h" +#include "vordemodscsettings.h" +#include "vordemodscsink.h" -VORDemodSCSink::VORDemodSCSink(const VORDemodSCSettings& settings, int subChannel, - MessageQueue *messageQueueToGUI) : +VORDemodSCSink::VORDemodSCSink() : m_channelFrequencyOffset(0), - m_outOfBand(true), - m_channelSampleRate(VORDEMOD_CHANNEL_SAMPLE_RATE), + m_channelSampleRate(VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE), m_audioSampleRate(48000), m_squelchCount(0), m_squelchOpen(false), @@ -42,29 +41,22 @@ VORDemodSCSink::VORDemodSCSink(const VORDemodSCSettings& settings, int subChanne m_magsqSum(0.0f), m_magsqPeak(0.0f), m_magsqCount(0), - m_messageQueueToGUI(messageQueueToGUI), m_volumeAGC(0.003), m_audioFifo(48000), m_refPrev(0.0f), m_movingAverageIdent(5000), m_prevBit(0), m_bitTime(0), - m_varGoertzel(30, VORDEMOD_CHANNEL_SAMPLE_RATE), - m_refGoertzel(30, VORDEMOD_CHANNEL_SAMPLE_RATE) + m_varGoertzel(30, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE), + m_refGoertzel(30, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE) { - m_audioBuffer.resize(1<<14); - m_audioBufferFill = 0; + m_audioBuffer.resize(1<<14); + m_audioBufferFill = 0; - m_magsq = 0.0; + m_magsq = 0.0; - qDebug() << "Sink " << subChannel; - if (subChannel >= 0) - { - m_subChannelId = subChannel; - m_vorFrequencyHz = settings.m_subChannelSettings[subChannel]->m_frequency; - - applySettings(settings, true); - } + applySettings(m_settings, true); + applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); } VORDemodSCSink::~VORDemodSCSink() @@ -75,9 +67,6 @@ void VORDemodSCSink::feed(const SampleVector::const_iterator& begin, const Sampl { Complex ci; - if (m_outOfBand) - return; - for (SampleVector::const_iterator it = begin; it != end; ++it) { Complex c(it->real(), it->imag()); @@ -148,7 +137,7 @@ void VORDemodSCSink::processOneAudioSample(Complex &ci) m_squelchOpen = (m_squelchCount >= (unsigned int)m_audioSampleRate / 20); - if (m_squelchOpen && !m_settings.m_audioMute && !m_settings.m_subChannelSettings.value(m_subChannelId)->m_audioMute) + if (m_squelchOpen && !m_settings.m_audioMute) { Real demod; @@ -219,7 +208,7 @@ void VORDemodSCSink::processOneSample(Complex &ci) // Calculate phase of 30Hz variable AM signal double varPhase; double varMag; - if (m_varGoertzel.size() == VORDEMOD_CHANNEL_SAMPLE_RATE - 1) + if (m_varGoertzel.size() == VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE - 1) { m_varGoertzel.goertzel(mag); varPhase = Units::radiansToDegress(m_varGoertzel.phase()); @@ -242,13 +231,13 @@ void VORDemodSCSink::processOneSample(Complex &ci) m_refPrev = fmfilt; // Calculate phase of 30Hz reference FM signal - if (m_refGoertzel.size() == VORDEMOD_CHANNEL_SAMPLE_RATE - 1) + if (m_refGoertzel.size() == VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE - 1) { m_refGoertzel.goertzel(phi); float phaseDeg = Units::radiansToDegress(m_refGoertzel.phase()); double refMag = m_refGoertzel.mag(); int groupDelay = (301-1)/2; - float filterPhaseShift = 360.0*30.0*groupDelay/VORDEMOD_CHANNEL_SAMPLE_RATE; + float filterPhaseShift = 360.0*30.0*groupDelay/VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE; float shiftedPhase = phaseDeg + filterPhaseShift; // Calculate difference in phase, which is the radial @@ -262,7 +251,7 @@ void VORDemodSCSink::processOneSample(Complex &ci) if (getMessageQueueToGUI()) { - VORDemodSCReport::MsgReportRadial *msg = VORDemodSCReport::MsgReportRadial::create(m_subChannelId, phaseDifference, refMag, varMag); + VORDemodSCReport::MsgReportRadial *msg = VORDemodSCReport::MsgReportRadial::create(phaseDifference, refMag, varMag); getMessageQueueToGUI()->push(msg); } @@ -316,7 +305,7 @@ void VORDemodSCSink::processOneSample(Complex &ci) qDebug() << m_ident << " " << Morse::toString(m_ident); if (getMessageQueueToGUI()) { - VORDemodSCReport::MsgReportIdent *msg = VORDemodSCReport::MsgReportIdent::create(m_subChannelId, m_ident); + VORDemodSCReport::MsgReportIdent *msg = VORDemodSCReport::MsgReportIdent::create(m_ident); getMessageQueueToGUI()->push(msg); } m_ident = ""; @@ -355,7 +344,7 @@ void VORDemodSCSink::processOneSample(Complex &ci) qDebug() << m_ident << " " << Morse::toString(m_ident); if (getMessageQueueToGUI()) { - VORDemodSCReport::MsgReportIdent *msg = VORDemodSCReport::MsgReportIdent::create(m_subChannelId, m_ident); + VORDemodSCReport::MsgReportIdent *msg = VORDemodSCReport::MsgReportIdent::create(m_ident); getMessageQueueToGUI()->push(msg); } m_ident = ""; @@ -380,17 +369,17 @@ void VORDemodSCSink::applyChannelSettings(int channelSampleRate, int channelFreq if ((m_channelSampleRate != channelSampleRate) || force) { - m_interpolator.create(16, channelSampleRate, VORDEMOD_CHANNEL_BANDWIDTH); + m_interpolator.create(16, channelSampleRate, VORDemodSCSettings::VORDEMOD_CHANNEL_BANDWIDTH); m_interpolatorDistanceRemain = 0; - m_interpolatorDistance = (Real) channelSampleRate / (Real) VORDEMOD_CHANNEL_SAMPLE_RATE; + m_interpolatorDistance = (Real) channelSampleRate / (Real) VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE; - m_samplesPerDot7wpm = VORDEMOD_CHANNEL_SAMPLE_RATE*60/(50*7); - m_samplesPerDot10wpm = VORDEMOD_CHANNEL_SAMPLE_RATE*60/(50*10); + m_samplesPerDot7wpm = VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE*60/(50*7); + m_samplesPerDot10wpm = VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE*60/(50*10); - m_ncoIdent.setFreq(-1020, VORDEMOD_CHANNEL_SAMPLE_RATE); // +-50Hz source offset allowed - m_ncoRef.setFreq(-9960, VORDEMOD_CHANNEL_SAMPLE_RATE); - m_lowpassIdent.create(301, VORDEMOD_CHANNEL_SAMPLE_RATE, 100.0f); - m_lowpassRef.create(301, VORDEMOD_CHANNEL_SAMPLE_RATE, 600.0f); // Max deviation is 480Hz + m_ncoIdent.setFreq(-1020, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE); // +-50Hz source offset allowed + m_ncoRef.setFreq(-9960, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE); + m_lowpassIdent.create(301, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE, 100.0f); + m_lowpassRef.create(301, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE, 600.0f); // Max deviation is 480Hz m_movingAverageIdent.resize(m_samplesPerDot10wpm/5); // Needs to be short enough for noise floor calculation m_binSampleCnt = 0; @@ -433,9 +422,9 @@ void VORDemodSCSink::applyAudioSampleRate(int sampleRate) qDebug("VORDemodSCSink::applyAudioSampleRate: sampleRate: %d m_channelSampleRate: %d", sampleRate, m_channelSampleRate); // (ICAO Annex 10 3.3.6.3) - Optional voice audio is 300Hz to 3kHz - m_audioInterpolator.create(16, VORDEMOD_CHANNEL_SAMPLE_RATE, 3000.0f); + m_audioInterpolator.create(16, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE, 3000.0f); m_audioInterpolatorDistanceRemain = 0; - m_audioInterpolatorDistance = (Real) VORDEMOD_CHANNEL_SAMPLE_RATE / (Real) sampleRate; + m_audioInterpolatorDistance = (Real) VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE / (Real) sampleRate; m_bandpass.create(301, sampleRate, 300.0f, 3000.0f); m_audioFifo.setSize(sampleRate); m_squelchDelayLine.resize(sampleRate/5); diff --git a/plugins/channelrx/demodvorsc/vordemodscsink.h b/plugins/channelrx/demodvorsc/vordemodscsink.h index 19715cb97..b5ec229f4 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsink.h +++ b/plugins/channelrx/demodvorsc/vordemodscsink.h @@ -34,18 +34,9 @@ #include -// Highest frequency is the FM subcarrier at up to ~11kHz -// However, old VORs can have 0.005% frequency offset, which is 6kHz -#define VORDEMOD_CHANNEL_BANDWIDTH 18000 -// Sample rate needs to be at least twice the above -// Also need to consider impact frequency resolution of Goertzel filters -// May as well make it a common audio rate, to possibly avoid decimation -#define VORDEMOD_CHANNEL_SAMPLE_RATE 48000 - class VORDemodSCSink : public ChannelSampleSink { public: - VORDemodSCSink(const VORDemodSCSettings& settings, int subChannel, - MessageQueue *messageQueueToGUI); + VORDemodSCSink(); ~VORDemodSCSink(); virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); @@ -78,11 +69,7 @@ public: m_magsqCount = 0; } - int m_subChannelId; // The id for the VOR this demod sink was created for - int m_vorFrequencyHz; // The VORs frequency - int m_frequencyOffset; // Different between sample source center frequeny and VOR center frequency int m_channelFrequencyOffset; - bool m_outOfBand; private: struct MagSqLevelsStore