Skip to content

Commit

Permalink
Merge pull request #2 from neilharvey94044/additional-sdrplay-features
Browse files Browse the repository at this point in the history
resolve JNR alignment and add RSPDX features
  • Loading branch information
Sammy1Am authored Feb 28, 2021
2 parents 17347cd + 629bdac commit 07ce0ec
Show file tree
Hide file tree
Showing 13 changed files with 525 additions and 154 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,16 @@ A Java wrapper for the [SDRplay API v3](https://www.sdrplay.com/) using [JNR-FFI
* Digital Audio Broadcast (DAB) Notch (RSP1A only)
* Bias-T (RSP1A only)

### Modifications v0.8.0 Neil Harvey 2021
* Fixed machine alignment of API Structs to make up for JNR bug/oversight
* RSPDX device specific functionality
* RF Notch
* DAB Notch
* Bias T
* Antenna Select
* Allow adjustment of IF Gain
* Enhance gain and overload event propagation


#### With Thanks To
The [SerCeMan/jnr-fuse](https://github.com/SerCeMan/jnr-fuse) project for being an excellent example of working, complex JNR code.
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies {
}

// Publishing Info
version = '0.7.0'
version = '0.8.0'

task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
Expand Down Expand Up @@ -56,7 +56,7 @@ publishing {
maven(MavenPublication) {
groupId = 'io.github.sammy1am'
artifactId = 'SDRplayJava'
version = '0.7.0'
version = '0.8.0'

from components.java
artifact sourcesJar {
Expand Down Expand Up @@ -88,8 +88,8 @@ bintray {
licenses = ['MIT']
vcsUrl = 'https://github.com/Sammy1Am/SDRplayJava.git'
version {
name = '0.6.0'
desc = '0.6.0'
name = '0.8.0'
desc = '0.8.0'
released = new Date()
}
}
Expand Down
193 changes: 172 additions & 21 deletions src/main/java/SDRplayJava/JNRMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,31 @@

import io.github.sammy1am.sdrplay.jnr.CallbackFnsT;
import io.github.sammy1am.sdrplay.jnr.CallbackFnsT.EventCallback;
import io.github.sammy1am.sdrplay.jnr.CallbackFnsT.EventT;
import io.github.sammy1am.sdrplay.jnr.CallbackFnsT.StreamCallback;
import io.github.sammy1am.sdrplay.jnr.DeviceParamsT;
import io.github.sammy1am.sdrplay.jnr.DeviceParamsT.DevParamsT;
import io.github.sammy1am.sdrplay.jnr.DeviceParamsT.RxChannelParamsT;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

import io.github.sammy1am.sdrplay.EventParameters;
import io.github.sammy1am.sdrplay.SDRplayAPI;
import io.github.sammy1am.sdrplay.jnr.CallbackFnsT.StreamCbParamsT;
import io.github.sammy1am.sdrplay.jnr.ControlParamsT.AgcControlT;
import io.github.sammy1am.sdrplay.jnr.SDRplayAPIJNR;
import io.github.sammy1am.sdrplay.jnr.SDRplayAPIJNR.DbgLvl_t;
import io.github.sammy1am.sdrplay.jnr.SDRplayAPIJNR.DeviceT;
import io.github.sammy1am.sdrplay.jnr.SDRplayAPIJNR.ErrT;
import io.github.sammy1am.sdrplay.jnr.SDRplayAPIJNR.ReasonForUpdateExtension1T;
import io.github.sammy1am.sdrplay.jnr.SDRplayAPIJNR.ReasonForUpdateT;
import io.github.sammy1am.sdrplay.jnr.TunerParamsT.Bw_MHzT;
import io.github.sammy1am.sdrplay.jnr.TunerParamsT.If_kHzT;
import io.github.sammy1am.sdrplay.jnr.TunerParamsT.LoModeT;
import io.github.sammy1am.sdrplay.jnr.TunerParamsT.TunerSelectT;
import jnr.ffi.Pointer;
import jnr.ffi.Struct;
Expand All @@ -29,11 +44,13 @@ public class JNRMain {

public static void main(String[] args) {

ErrT err = ErrT.Success;

SDRplayAPIJNR API = SDRplayAPI.getJNRInstance();
jnr.ffi.Runtime runtime = SDRplayAPI.getJNRRuntime();

// Open and get API
ErrT err = API.sdrplay_api_Open();
err = API.sdrplay_api_Open();
FloatByReference apiVer = new FloatByReference();
err = API.sdrplay_api_ApiVersion(apiVer);
System.out.println("API: "+ apiVer.floatValue());
Expand All @@ -43,61 +60,195 @@ public static void main(String[] args) {
IntByReference numDevices = new IntByReference();
err = API.sdrplay_api_GetDevices(devices, numDevices, 16);

//Disable heartbeats - to allow debugging without timing out
//err = API.sdrplay_api_DisableHeartbeat();
//System.out.println("Disable Heartbeat: "+ err.name());

// Select device
err = API.sdrplay_api_SelectDevice(devices[0]);

// Set Debug
err = API.sdrplay_api_DebugEnable(devices[0].dev.get(), DbgLvl_t.DbgLvl_Verbose);
//err = API.sdrplay_api_DebugEnable(devices[0].dev.get(), DbgLvl_t.DbgLvl_Message);
//err = API.sdrplay_api_DebugEnable(devices[0].dev.get(), DbgLvl_t.DbgLvl_Warning);

// Init device
CallbackFnsT callbacks = new CallbackFnsT(runtime);


StreamCallback scba = new StreamCallback() {
@Override
public void call(Pointer xi, Pointer xq, StreamCbParamsT params, int numSamples, int reset, Pointer cbContext) {
System.out.println("Got A!");
//System.out.println("Got A!");
}
};

StreamCallback scbb = new StreamCallback() {
@Override
public void call(Pointer xi, Pointer xq, StreamCbParamsT params, int numSamples, int reset, Pointer cbContext) {
System.out.println("Got A!");
System.out.println("Got B!");
}
};

EventCallback ecb = new EventCallback() {

@Override
public void call(CallbackFnsT.EventT eventId, TunerSelectT tuner, CallbackFnsT.EventParamsT params, jnr.ffi.Pointer cbContext) {
System.out.println("Got Event!");
public void call(EventT eventId, TunerSelectT tuner, EventParameters params) {
switch(eventId)
{
case GainChange:
System.out.printf("%s gRdB=%d lnaGRdB=%d systemGain=%.2f\n",
eventId.name(), params.gainParams.gRdB, params.gainParams.lnaGRdB,
params.gainParams.currGain);
default:
break;
}
}
};



callbacks.StreamACbFn.set(scba);
callbacks.StreamBCbFn.set(scbb);
callbacks.EventCbFn.set(ecb);


err = API.sdrplay_api_Init(devices[0].dev.get(), callbacks, null);

// Get Params
PointerByReference deviceParamsPBR = new PointerByReference();

err = API.sdrplay_api_GetDeviceParams(devices[0].dev.get(), deviceParamsPBR);

DeviceParamsT deviceParams = new DeviceParamsT(runtime);
deviceParams.useMemory(deviceParamsPBR.getValue());

//Set starting device parameters
deviceParams.devParams.get().fsFreq.fsHz.set(8_000_000);

//Set tuner parameters
deviceParams.rxChannelA.get().tunerParams.rfFreq.rfHz.set(700_000_000);
deviceParams.rxChannelA.get().tunerParams.gain.gRdB.set(20);
deviceParams.rxChannelA.get().tunerParams.gain.LNAstate.set(5);
deviceParams.rxChannelA.get().ctrlParams.agc.enable.set(AgcControlT.AGC_DISABLE);
deviceParams.rxChannelA.get().tunerParams.ifType.set(If_kHzT.IF_Zero);
deviceParams.rxChannelA.get().tunerParams.bwType.set(Bw_MHzT.BW_8_000);

//Set callback functions
callbacks.StreamACbFn.set(scba);
callbacks.StreamBCbFn.set(scbb);
callbacks.EventCbFn.set(ecb);

//Init device
err = API.sdrplay_api_Init(devices[0].dev.get(), callbacks, null);
System.out.println("Error Return from sdrplay_api_Init: " + err.name());

deviceParams.devParams.get().ppm.set(3.0);

// Update
err = API.sdrplay_api_Update(devices[0].dev.get(), TunerSelectT.Tuner_A, ReasonForUpdateT.Update_Dev_Ppm, ReasonForUpdateExtension1T.Update_Ext1_None);



int gr = 20;
java.util.Scanner s = new java.util.Scanner(System.in);
String c = "!";
while(!c.contentEquals("q"))
{
System.out.println("Enter one command [GRU][GRD][BN]:");
c = s.nextLine();
switch(c)
{
case "GRU":
{
//Set IF Gain up - this value is automatically changed if AGC is on, so disable AGC first
if (gr < 59) {
gr += 1;
deviceParams.rxChannelA.get().tunerParams.gain.gRdB.set(gr) ;
err = API.sdrplay_api_Update(devices[0].dev.get(), TunerSelectT.Tuner_A, ReasonForUpdateT.Update_Tuner_Gr, ReasonForUpdateExtension1T.Update_Ext1_None);
System.out.println("Error Return from IF Gain Update: " + err.name());
}
break;
}
case "GRD":
{
//Set IF Gain down - this value is automatically changed if AGC is on, so disable AGC first
if (gr > 20) {
gr -= 1;
deviceParams.rxChannelA.get().tunerParams.gain.gRdB.set(gr) ;
err = API.sdrplay_api_Update(devices[0].dev.get(), TunerSelectT.Tuner_A, ReasonForUpdateT.Update_Tuner_Gr, ReasonForUpdateExtension1T.Update_Ext1_None);
System.out.println("Error Return from IF Gain Update: " + err.name());
}
break;
}
case "BN":
{
//Enable RSPdx rfNotch
deviceParams.devParams.get().rspdxParams.rfNotchEnable.set(1);
err = API.sdrplay_api_Update(devices[0].dev.get(), TunerSelectT.Tuner_A, ReasonForUpdateT.Update_None, ReasonForUpdateExtension1T.Update_RspDx_RfNotchControl);
System.out.println("Error return from rfNotch Update: "+err.name());
break;
}
}

}

// Examine raw channel data after we have updated
RxChannelParamsT channelA = deviceParams.rxChannelA.get();
Pointer ptr = Struct.getMemory(channelA);
byte tunerdata[] = new byte[Struct.size(channelA)];
ptr.get(0, tunerdata, 0, Struct.size(channelA) );
System.out.println("Should have byte array now.");
WriteRawFile(tunerdata, "tunerdata.txt");


System.out.println("Closing API");
err = API.sdrplay_api_Uninit(devices[0].dev.get());
err = API.sdrplay_api_UnlockDeviceApi();
err = API.sdrplay_api_Close();

System.out.println("We're done!");
System.exit(0);
}

protected static void WriteRawFile(byte[] buf, String binaryOut)
{
try (OutputStream imageFileOut = Files.newOutputStream(Paths.get(binaryOut), StandardOpenOption.CREATE))
{
System.out.println("About to write to: " + binaryOut);

if(buf.length > 0) {
imageFileOut.write(buf, 0, buf.length);
}
}
catch (IOException ex) {
System.err.println(ex);
}
}

// These values are important for determining machine alignment and necessary padding in the Structs.
// The JNR library does not fully implement machine alignment for Structs, it has a bug that leaves
// necessary padding off the end of structs which causes problems when the structs are stacked.
// Compare to actual c sizes the SDRPlay API is compiled to, add padding as required.
protected static void ShowStructSizes(DeviceParamsT deviceParams)
{
//Examine Dev Parms Sizes
DevParamsT dev = deviceParams.devParams.get();

System.out.println("Size of devParams:" + Integer.toString( Struct.size(dev)) + ":" + Integer.toString(Struct.alignment(dev)));

System.out.println("Size of fsFreq:" + Integer.toString( Struct.size(dev.fsFreq)) + ":" + Integer.toString(Struct.alignment(dev.fsFreq)));
System.out.println("Size of SyncUpdateT:" + Integer.toString( Struct.size(dev.syncUpdate)) + ":" + Integer.toString(Struct.alignment(dev.syncUpdate)));
System.out.println("Size of ResetFlagsT:" + Integer.toString( Struct.size(dev.resetFlags)) + ":" + Integer.toString(Struct.alignment(dev.resetFlags)));

System.out.println("Size of Rsp1aParamsT:" + Integer.toString(Struct.size(dev.rsp1aParams)) + ":" + Integer.toString(Struct.alignment(dev.rsp1aParams)));
System.out.println("Size of Rsp2ParamsT:" + Integer.toString(Struct.size(dev.rsp2Params)) + ":" + Integer.toString(Struct.alignment(dev.rsp2Params)));
System.out.println("Size of RspDuoParamsT:" + Integer.toString(Struct.size(dev.rspduoParams)) + ":" + Integer.toString(Struct.alignment(dev.rspduoParams)) );
System.out.println("Size of RspDxParamsT:" + Integer.toString(Struct.size(dev.rspdxParams)) + ":" + Integer.toString(Struct.alignment(dev.rspdxParams)));

RxChannelParamsT channelA = deviceParams.rxChannelA.get();

System.out.println("Size of channel1A:" + Integer.toString( Struct.size(channelA)) );
System.out.println("Size of sdrplay_api_TunerParamsT:" + Integer.toString( Struct.size(channelA.tunerParams)) + ":" + Integer.toString(Struct.alignment(channelA.tunerParams)));
System.out.println("Size of sdrplay_api_ControlParamsT:" + Integer.toString( Struct.size(channelA.ctrlParams)) + ":" + Integer.toString(Struct.alignment(channelA.ctrlParams)));
System.out.println("Size of sdrplay_api_Rsp1aTunerParamsT:" + Integer.toString( Struct.size(channelA.rsp1aTunerParams)) + ":" + Integer.toString(Struct.alignment(channelA.rsp1aTunerParams)));
System.out.println("Size of sdrplay_api_Rsp2TunerParamsT:" + Integer.toString( Struct.size(channelA.rsp2TunerParams)) + ":" + Integer.toString(Struct.alignment(channelA.rsp2TunerParams)));
System.out.println("Size of sdrplay_api_RspDuoTunerParamsT:" + Integer.toString( Struct.size(channelA.rspduoTunerParams)) + ":" + Integer.toString(Struct.alignment(channelA.rspduoTunerParams)));
System.out.println("Size of sdrplay_api_RspDxTunerParamsT:" + Integer.toString( Struct.size(channelA.rspdxTunerParams)) + ":" + Integer.toString(Struct.alignment(channelA.rspdxTunerParams)));

System.out.println("Size of sdrplay_api_GainT:" + Integer.toString( Struct.size(channelA.tunerParams.gain)) + ":" + Integer.toString(Struct.alignment(channelA.tunerParams.gain)));
System.out.println("Size of sdrplay_api_GainValuesT:" + Integer.toString( Struct.size(channelA.tunerParams.gain.gainVals)) + ":" + Integer.toString(Struct.alignment(channelA.tunerParams.gain.gainVals)));

System.out.println("Size of sdrplay_api_RfFreqT:" + Integer.toString( Struct.size(channelA.tunerParams.rfFreq)) + ":" + Integer.toString(Struct.alignment(channelA.tunerParams.rfFreq)));
System.out.println("Size of sdrplay_api_DcOffsetTunerT:" + Integer.toString( Struct.size(channelA.tunerParams.dcOffsetTuner)) + ":" + Integer.toString(Struct.alignment(channelA.tunerParams.dcOffsetTuner)));
System.out.println("Size of sdrplay_api_DcOffsetT:" + Integer.toString( Struct.size(channelA.ctrlParams.dcOffset)) + ":" + Integer.toString(Struct.alignment(channelA.ctrlParams.dcOffset)));
System.out.println("Size of sdrplay_api_DecimationT:" + Integer.toString( Struct.size(channelA.ctrlParams.decimation)) + ":" + Integer.toString(Struct.alignment(channelA.ctrlParams.decimation)));
System.out.println("Size of sdrplay_api_AgcT:" + Integer.toString( Struct.size(channelA.ctrlParams.agc)) + ":" + Integer.toString(Struct.alignment(channelA.ctrlParams.agc)));

}

}
24 changes: 17 additions & 7 deletions src/main/java/io/github/sammy1am/sdrplay/EventParameters.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.sammy1am.sdrplay;

import io.github.sammy1am.sdrplay.jnr.CallbackFnsT.EventParamsT;
import io.github.sammy1am.sdrplay.jnr.CallbackFnsT.EventT;
import io.github.sammy1am.sdrplay.jnr.CallbackFnsT.GainCbParamT;
import io.github.sammy1am.sdrplay.jnr.CallbackFnsT.PowerOverloadCbEventIdT;
import io.github.sammy1am.sdrplay.jnr.CallbackFnsT.PowerOverloadCbParamT;
Expand All @@ -14,14 +15,23 @@
* @author Sammy1Am
*/
public class EventParameters {
public final GainCbParam gainParams;
public final PowerOverloadCbParam powerOverloadParams;
public final RspDuoModeCbParam rspDuoModeParams;
public GainCbParam gainParams = null;
public PowerOverloadCbParam powerOverloadParams = null;
public RspDuoModeCbParam rspDuoModeParams = null;

public EventParameters(EventParamsT eventParams) {
gainParams = new GainCbParam(eventParams.gainParams);
powerOverloadParams = new PowerOverloadCbParam(eventParams.powerOverloadParams);
rspDuoModeParams = new RspDuoModeCbParam(eventParams.rspDuoModeParams);
public EventParameters(EventT eventId, EventParamsT eventParams) {
switch(eventId) {
case GainChange:
gainParams = new GainCbParam(eventParams.gainParams);
break;
case PowerOverloadChange:
powerOverloadParams = new PowerOverloadCbParam(eventParams.powerOverloadParams);
break;
case RspDuoModeChange:
rspDuoModeParams = new RspDuoModeCbParam(eventParams.rspDuoModeParams);
break;
default:
}
}

public static class GainCbParam {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/io/github/sammy1am/sdrplay/SDRplayAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.github.sammy1am.sdrplay.model.RSPDuo;
import io.github.sammy1am.sdrplay.model.RSP1A;
import io.github.sammy1am.sdrplay.model.RSPDX;
import io.github.sammy1am.sdrplay.jnr.SDRplayAPIJNR;
import io.github.sammy1am.sdrplay.jnr.SDRplayAPIJNR.ErrT;
import java.util.ArrayList;
Expand Down Expand Up @@ -73,6 +74,9 @@ public static List<SDRplayDevice> getDevices(int maximumDevices) {
case 255:
returnDevices.add(new RSP1A(devices[d]));
break;
case 4:
returnDevices.add(new RSPDX(devices[d]));
break;
case 1:
default:
returnDevices.add(new SDRplayDevice(devices[d]));
Expand Down
Loading

0 comments on commit 07ce0ec

Please sign in to comment.