Skip to content

Commit

Permalink
feat: added support for new RF Explorer PLUS models
Browse files Browse the repository at this point in the history
  • Loading branch information
berkon committed Jun 5, 2024
1 parent e8d31c3 commit 67244f7
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
V2.1.0
##New Features
* Added support for RF Explorer Plus models
* Added logger. App logs can be downloaded via the "Help" menu.
* Frequency range can now be entered manually (via "Band" menu or f-hotkey)
* Added hotkeys 1-9 for switching bands. Use "Band" menu or SHIFT-<hotkey> to save current band
Expand Down
61 changes: 52 additions & 9 deletions renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ let formValid = false

let curKeyInputTarget = '';
let keyInputTargets = {
MANUAL_BAND_SETTINGS: 'MANUAL_BAND_SETTINGS'
MANUAL_BAND_SETTINGS: 'MANUAL_BAND_SETTINGS',
SWEEP_POINT_SETTINGS: 'SWEEP_POINT_SETTINGS'
}

document.getElementById('donate-button').addEventListener ('click', () => openDonateWindow() )
Expand Down Expand Up @@ -1414,26 +1415,36 @@ ipcRenderer.on ( 'SET_SCAN_DEVICE', async (event, message) => {
})

ipcRenderer.on ( 'DEVICE_SETTINGS', async (event, message) => {
if ( scanDevice instanceof RFExplorer ) {
if ( scanDevice instanceof RFExplorer && scanDevice.constructor.MODEL === 'BASIC' ) {
showPopup (
'warning',
'POPUP_CAT_SETTINGS',
"No settings available",
"This device does not have configurable settings!",
['Ok']
)
} else if ( scanDevice instanceof TinySA ) {
} else if (
(scanDevice instanceof RFExplorer && scanDevice.constructor.MODEL === 'PLUS') ||
scanDevice instanceof TinySA
) {
Swal.fire({
title: "Enter number of sweep points",
input: "text",
width: '200px',
html:'<p style="font-family: arial">Please enter a value between ' + scanDevice.getMinSweepPoints() + ' and ' + scanDevice.getMaxSweepPoints() + '</p>' +
'<div><h2 class="swal2-title sweetalert2-title" style="display: inline; margin-left: 0;">Sweep points</h2><input id="swal-input" class="swal2-input"></div>',
width: '600px',
showCancelButton: true,
confirmButtonText: "Ok",
confirmButtonColor: "#0099ff",
stopKeydownPropagation: false,
customClass: {
title: 'sweetalert2-title',
validationMessage: 'sweetalert2-validation-message'
},
preConfirm: function () {
return new Promise(function (resolve) {
resolve(document.getElementById('swal-input').value)
})
},
inputValidator: value => {
return new Promise( resolve => {
if ( !isNaN(value) ) {
Expand All @@ -1444,13 +1455,31 @@ ipcRenderer.on ( 'DEVICE_SETTINGS', async (event, message) => {
});
}
}).then ( result => {
curKeyInputTarget = '';

if (result.isConfirmed) {
global.SWEEP_POINTS = result.value
log.info ( "Number of sweep points was set to: " + global.SWEEP_POINTS )
configStore.set ( 'sweep_points', global.SWEEP_POINTS )
scanDevice.setConfiguration ( global.START_FREQ, global.STOP_FREQ, global.SWEEP_POINTS );
if ( scanDevice instanceof RFExplorer && scanDevice.constructor.MODEL === 'PLUS' ) {
global.SWEEP_POINTS = parseInt(result.value)
configStore.set ( 'sweep_points', global.SWEEP_POINTS )
scanDevice.setSweepPoints ( parseInt(global.SWEEP_POINTS) )
} else if ( scanDevice instanceof TinySA ) {
global.SWEEP_POINTS = result.value
log.info ( "Setting number of sweep points to: " + global.SWEEP_POINTS )
configStore.set ( 'sweep_points', global.SWEEP_POINTS )
scanDevice.setConfiguration ( global.START_FREQ, global.STOP_FREQ, global.SWEEP_POINTS );
} else {
log.error ( `Unable to set sweep points! Unknown device: ${scanDevice.constructor.NAME} ${scanDevice.constructor.HW_TYPE} ${scanDevice.constructor.MODEL}` )
}
}
})

curKeyInputTarget = keyInputTargets.SWEEP_POINT_SETTINGS;
setTimeout(()=>{
document.getElementsByClassName('swal2-confirm')[0].disabled = true
document.getElementById('swal-input').focus()
}, 200);
} else {
log.error ( `Unknown device type: ${scanDevice.constructor.NAME}, ${scanDevice.constructor.HW_TYPE}, ${scanDevice.constructor.MODEL}`)
}
})

Expand Down Expand Up @@ -1742,6 +1771,7 @@ document.addEventListener ( "keydown", async e => {
switch ( curKeyInputTarget ) {
// Manual frequency band settings modal
case keyInputTargets.MANUAL_BAND_SETTINGS:
case keyInputTargets.SWEEP_POINT_SETTINGS:
switch ( e.key ) {
case 'Enter':
if ( formValid ) {
Expand Down Expand Up @@ -1923,6 +1953,19 @@ document.addEventListener ( "keyup", async e => {
}
} break;

case keyInputTargets.SWEEP_POINT_SETTINGS: {
formValid = false

if ( !scanDevice.isValidSweepPointRange(document.getElementById('swal-input').value) ) {
document.getElementById('swal-input').style.backgroundColor = "#ffb6b6"
document.getElementsByClassName('swal2-confirm')[0].disabled = true
} else {
formValid = true
document.getElementById('swal-input').style.backgroundColor = "unset"
document.getElementsByClassName('swal2-confirm')[0].disabled = false
}
} break;

default:
}
})
Expand Down
129 changes: 119 additions & 10 deletions scan_devices/rf_explorer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,35 @@
const { DelimiterParser } = require ( '@serialport/parser-delimiter');

class RFExplorer {
static NAME = 'RF Explorer';
static HW_TYPE = 'RF_EXPLORER';
static BAUD_RATE = 500000;
static MIN_SPAN = 112000
static NAME = 'RF Explorer'; // Basic device name
static MODEL = '' // This divides devices with the same base NAME and HW_TYPE into specific models
// The device type shares the same API with similar devices. This is e.g. needed for sw to know how a
// device can be contacted via the serial port.
static HW_TYPE = 'RF_EXPLORER';
static BAUD_RATE = 500000;

static MIN_SPAN_BASIC = 112000; // 112 kHz
static MAX_SPAN_BASIC = 600000000; // 600 MHz
static MIN_SWEEP_POINTS_BASIC = 112
static MAX_SWEEP_POINTS_BASIC = 112

static MIN_SPAN_PLUS = 112000; // 112 kHz
static MAX_SPAN_PLUS = 192500000; // 192.5 MHz
static MIN_SWEEP_POINTS_PLUS = 112
static MAX_SWEEP_POINTS_PLUS = 65535

static deviceCommands = {
GET_CONFIG: '#0C0', // Get current configuration
SET_CONFIG: '#0C2-F:', // Set configuration
HOLD: '#0CH' // Tell the device to stop sending scan data
HOLD: '#0CH', // Tell the device to stop sending scan data
SET_SWEEP_POINTS_LARGE :'#0Cj' // Set number of sweep points up to 65536
};
static deviceEvents = {
NAME: 'RF Explorer',
DEVICE_DATA: '#C2-M:', // '#C2-M:' = Main model code, expansion model code and firmware version
CONFIG_DATA: '#C2-F:', // '#C2-F:' = config data from scan device
SCAN_DATA: '$S', // '$S' = sweep data, 'p' = ASCII code 112 ( 112 sweep points will be received) 'à' = ASCII code 224 ( 224 sweep points will be received)
SCAN_DATA: '$S', // '$S' = sweep data, 'p' = ASCII code 112 ( 112 sweep points will be received) 'à' = ASCII code 224 ( 224 sweep points will be received)
SCAN_DATA_LARGE: '$z', // '$s' = sweep data, 'p' = ASCII code 112 ( 112 sweep points will be received) 'à' = ASCII code 224 ( 224 sweep points will be received)
CALIBRATION_DATA: '#CAL:', // Calibration data ?? (nothing in the docs)
QUALITY_DATA: '#QA:', // Quality data ?? (nothing in the docs)
SERIAL_NUMBER: '#Sn' // Serial number
Expand All @@ -31,6 +46,40 @@ class RFExplorer {
this.port = port
}

getMinSweepPoints () {
switch ( RFExplorer.MODEL ) {
case 'BASIC': return RFExplorer.MIN_SWEEP_POINTS_BASIC;
case 'PLUS' : return RFExplorer.MIN_SWEEP_POINTS_PLUS;
}
}

getMaxSweepPoints () {
switch ( RFExplorer.MODEL ) {
case 'BASIC': return RFExplorer.MAX_SWEEP_POINTS_BASIC;
case 'PLUS' : return RFExplorer.MAX_SWEEP_POINTS_PLUS;
}
}

isValidSweepPointRange ( numOfSweepPoints ) {
switch ( RFExplorer.MODEL ) {
case 'BASIC':
if ( numOfSweepPoints >= RFExplorer.MIN_SWEEP_POINTS_BASIC &&
numOfSweepPoints <= RFExplorer.MAX_SWEEP_POINTS_BASIC ) {
return true
} else {
return false
}

case 'PLUS':
if ( numOfSweepPoints >= RFExplorer.MIN_SWEEP_POINTS_PLUS &&
numOfSweepPoints <= RFExplorer.MAX_SWEEP_POINTS_PLUS ) {
return true
} else {
return false
}
}
}

getConfiguration () {
// IMPORTANT: After requesting the configuration data, the device immediately starts sending scan data.
// No additional command is required!
Expand Down Expand Up @@ -72,13 +121,43 @@ class RFExplorer {
}
}

async setSweepPoints ( numberOfSweepPoints ) {
log.info ( `Setting number of sweep points to ${numberOfSweepPoints} ...` )
// Second character will be replaced by a binary lenght value
// '00' is just a placeholder for two bytes in the buffer which will be replaced by MSB/LSB
let sendBuf = Buffer.from ( RFExplorer.deviceCommands.SET_SWEEP_POINTS_LARGE + '00', 'ascii' );
sendBuf.writeUInt8 ( 0x6, 1 );
sendBuf.writeUInt8 ( (numberOfSweepPoints & 0xFF00) >> 8, 4 ); // MSB
sendBuf.writeUInt8 ( numberOfSweepPoints & 0x00FF , 5 ); // LSB
await this.port.writePromise ( sendBuf, 'ascii' )
}

setHandler (data$) {
log.info ( `Setting handler for ${RFExplorer.NAME} data receiption ... ` )
const parser = this.port.pipe(new DelimiterParser({ delimiter: '\r\n' }))

parser.on ( 'data', (res) => {
let buf = String.fromCharCode.apply ( null, res )

// When a command is sent to the device, on newer models of RF Explorer (PLUS variant),
// the device is transmitting a so called EOS sequence ('End Of Sweep') to acknowledge
// the end of an ongoing sweep. This sequence is FF FE FF FE 00 (as string: 'ÿþÿþ ').
// We need to strip these characters before processing the string.
let eosSequences = [
{ sequence: 'ÿþÿþ\x00#', offset: 5 },
{ sequence: 'þ\x00#' , offset: 2 },
{ sequence: 'ÿþÿþ\x00$', offset: 5 },
{ sequence: 'þ\x00$' , offset: 2 }
]

for ( const eosSequence of eosSequences) {
let eosIdx = buf.indexOf ( eosSequence.sequence )

if ( eosIdx !== -1 ) {
buf = buf.substring ( eosIdx + eosSequence.offset )
}
}

for ( let deviceEventType in this.constructor.deviceEvents ) {
let deviceEvent = this.constructor.deviceEvents[deviceEventType]

Expand All @@ -94,8 +173,26 @@ class RFExplorer {
return
}

const dataLength = parseInt ( deviceEvent === RFExplorer.deviceEvents.SCAN_DATA ? buf[deviceEvent.length] : (buf.length - deviceEvent.length) )
const data = buf.substring ( deviceEvent === RFExplorer.deviceEvents.SCAN_DATA ? (deviceEvent.length + 1) : deviceEvent.length ) // +1 to exclude the byte following the message ID which contains the number of sweep points
let dataLength = 0;
let data = ''

switch (deviceEvent) {
case RFExplorer.deviceEvents.SCAN_DATA:
dataLength = buf.charCodeAt(deviceEvent.length)
data = buf.substring ( deviceEvent.length + 1 ) // +1 to exclude the byte following the message ID which contains the number of sweep points
break

case RFExplorer.deviceEvents.SCAN_DATA_LARGE:
const MSB = buf.charCodeAt(deviceEvent.length)
const LSB = buf.charCodeAt(deviceEvent.length + 1)
dataLength = (MSB << 8) | LSB;
data = buf.substring ( deviceEvent.length + 2 ) // +2 to exclude the two bytes following the message ID which contain the number of sweep points
break

default:
dataLength = buf.length - deviceEvent.length
data = buf.substring ( deviceEvent.length )
}

switch ( deviceEvent ) {
case this.constructor.deviceEvents.CONFIG_DATA: // Received config data from scan device
Expand Down Expand Up @@ -153,16 +250,19 @@ class RFExplorer {
case 4: mainModelString = '2.4G' ; break;
case 5: mainModelString = 'WSUB3G' ; break;
case 6: mainModelString = '6G' ; break;
case 10: mainModelString = 'WSUB1G_PLUS'; break;
case 10: mainModelString = 'WSUB1G_PLUS';
RFExplorer.MODEL = 'PLUS'
break;
case 60: mainModelString = 'RFEGEN' ; break;
default:
RFExplorer.MODEL = 'BASIC'
log.error( `Unknown 'RF Explorer' model code: ${mainModelCode}` )
}

switch (expansionModelCode) {
case 4: expansionModelString = '2.4G' ; break;
case 5: expansionModelString = 'WSUB3G' ; break;
case 12: expansionModelString = '2.4G Gen 2' ; break;
case 12: expansionModelString = 'WSUB3G PLUS' ; break;
case 255: expansionModelString = 'No expansion model'; break;
default:
log.error( `Unknown expansion model code: ${expansionModelCode}` )
Expand All @@ -181,6 +281,15 @@ class RFExplorer {
}])
}
break

case this.constructor.deviceEvents.SCAN_DATA_LARGE:
if ( this.received_config_data ) {
data$.next([{
type: "SCAN_DATA",
values: data
}])
}
break
}
}
}
Expand Down
Loading

0 comments on commit 67244f7

Please sign in to comment.