Voltage Measurement through Channels in PSLab
The Pocket Science Lab multimeter has got three channels namely CH1,CH2 and CH3 with different ranges for measuring the voltages.This blog will give a brief description on how we measure voltages in channels.
Measuring Voltages at channels can be divided into three parts:-
-
-
-
- Communication between between device and Android.
- Setting up analog channel (analog constants)
- Voltage measuring function of android.
-
-
Communication between PSLab device and Android App
The communication between the PSLab device and Android occurs through the help of UsbManger package of CommunicationHandler class of the app. The main two functions involved in the communication are read and write functions in which we send particular number of bytes and then we receive certain bytes.
The read function :-
public int read(byte[] dest, int bytesToBeRead, int timeoutMillis) throws IOException {
int numBytesRead = 0;
//synchronized (mReadBufferLock) {
int readNow;
Log.v(TAG, "TO read : " + bytesToBeRead);
int bytesToBeReadTemp = bytesToBeRead;
while (numBytesRead < bytesToBeRead) {
readNow = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, bytesToBeReadTemp, timeoutMillis);
if (readNow < 0) {
Log.e(TAG, "Read Error: " + bytesToBeReadTemp);
return numBytesRead;
} else {
//Log.v(TAG, "Read something" + mReadBuffer);
System.arraycopy(mReadBuffer, 0, dest, numBytesRead, readNow);
numBytesRead += readNow;
bytesToBeReadTemp -= readNow;
//Log.v(TAG, "READ : " + numBytesRead);
//Log.v(TAG, "REMAINING: " + bytesToBeRead);
}
}
//}
Log.v("Bytes Read", "" + numBytesRead);
return numBytesRead;
}
Similarly the write function is –
public int write(byte[] src, int timeoutMillis) throws IOException {
if (Build.VERSION.SDK_INT < 18) {
return writeSupportAPI(src, timeoutMillis);
}
int written = 0;
while (written < src.length) {
int writeLength, amtWritten;
//synchronized (mWriteBufferLock) {
writeLength = Math.min(mWriteBuffer.length, src.length - written);
// bulk transfer supports offset from API 18
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, src, written, writeLength, timeoutMillis);
//}
if (amtWritten < 0) {
throw new IOException("Error writing " + writeLength +
" bytes at offset " + written + " length=" + src.length);
}
written += amtWritten;
}
return written;
}
Although these are the core functions used for communication but the data received through these functions are further processed using another class known as PacketHandler. In the PacketHandler class also there are two major functions i.e sendByte and getByte(), these are the main functions which are further used in other classes for communication.
The sendByte function:-
public void sendByte(int val) throws IOException {
if (!connected) {
throw new IOException("Device not connected");
}
if (!loadBurst) {
try {
mCommunicationHandler.write(new byte[] {
(byte)(val & 0xff), (byte)((val >> 8) & 0xff)
}, timeout);
} catch (IOException e) {
Log.e("Error in sending int", e.toString());
e.printStackTrace();
}
} else {
burstBuffer.put(new byte[] {
(byte)(val & 0xff), (byte)((val >> 8) & 0xff)
});
}
}
As we can see that in this function also the main function used is the write function of communicationHandler but in this class the data is further processed.
Setting Up the Analog Constants
For setting up the ranges, gains and other properties of channels, a different class of AnalogConstants is implemented in the android app, in this class all the properties which are used by the channels are defined which are further used in the sendByte() functions for communication.
public class AnalogConstants {
public double[] gains = {1, 2, 4, 5, 8, 10, 16, 32, 1 / 11.};
public String[] allAnalogChannels = {"CH1", "CH2", "CH3", "MIC", "CAP", "SEN", "AN8"};
public String[] biPolars = {"CH1", "CH2", "CH3", "MIC"};
public Map<String, double[]> inputRanges = new HashMap<>();
public Map<String, Integer> picADCMultiplex = new HashMap<>();
public AnalogConstants() {
inputRanges.put("CH1", new double[]{16.5, -16.5});
inputRanges.put("CH2", new double[]{16.5, -16.5});
inputRanges.put("CH3", new double[]{-3.3, 3.3});
inputRanges.put("MIC", new double[]{-3.3, 3.3});
inputRanges.put("CAP", new double[]{0, 3.3});
inputRanges.put("SEN", new double[]{0, 3.3});
inputRanges.put("AN8", new double[]{0, 3.3});
picADCMultiplex.put("CH1", 3);
picADCMultiplex.put("CH2", 0);
picADCMultiplex.put("CH3", 1);
picADCMultiplex.put("MIC", 2);
picADCMultiplex.put("AN4", 4);
picADCMultiplex.put("SEN", 7);
picADCMultiplex.put("CAP", 5);
picADCMultiplex.put("AN8", 8);
}
}
Also in the AnalogInput sources class many other properties such as CHOSA( a variable assigned to denote the analog to decimal conversion constant of each channel) are also defined
public AnalogInputSource(String channelName) {
AnalogConstants analogConstants = new AnalogConstants();
this.channelName = channelName;
range = analogConstants.inputRanges.get(channelName);
gainValues = analogConstants.gains;
this.CHOSA = analogConstants.picADCMultiplex.get(channelName);
calPoly10 = new PolynomialFunction(new double[] {
0.,
3.3 / 1023,
0.
});
calPoly12 = new PolynomialFunction(new double[] {
0.,
3.3 / 4095,
0.
});
if (range[1] - range[0] < 0) {
inverted = true;
inversion = -1;
}
if (channelName.equals("CH1")) {
gainEnabled = true;
gainPGA = 1;
gain = 0;
} else if (channelName.equals("CH2")) {
gainEnabled = true;
gainPGA = 2;
gain = 0;
}
gain = 0;
regenerateCalibration();
}
Also in this constructor a polynomial function is also called which further plays an important role in measuring voltage as it is through this polynomial function we get the voltage of channels in the science lab class , also it is also used in oscilloscope for plotting the graph . So this was the setup of analog channels.
Voltage Measuring Functions
There are two major functions for measuring voltages which are present in the scienceLab class
- getAverageVoltage
- getRawableVoltage
Here are the functions
private double getRawAverageVoltage(String channelName) {
try {
int chosa = this.calcCHOSA(channelName);
mPacketHandler.sendByte(mCommandsProto.ADC);
mPacketHandler.sendByte(mCommandsProto.GET_VOLTAGE_SUMMED);
mPacketHandler.sendByte(chosa);
int vSum = mPacketHandler.getVoltageSummation();
mPacketHandler.getAcknowledgement();
return vSum / 16.0;
} catch (IOException | NullPointerException e) {
e.printStackTrace();
Log.e(TAG, "Error in getRawAverageVoltage");
}
return 0;
}
This is the major function which takes the data from the communicationHandler class via packetHandler. Further this function is used in the getAverageVoltage function.
private double getAverageVoltage(String channelName, Integer sample) {
if (sample == null) sample = 1;
PolynomialFunction poly;
double sum = 0;
poly = analogInputSources.get(channelName).calPoly12;
ArrayList < Double > vals = new ArrayList < > ();
for (int i = 0; i < sample; i++) {
vals.add(getRawAverageVoltage(channelName));
}
for (int j = 0; j < vals.size(); j++) {
sum = sum + poly.value(vals.get(j));
}
return sum / vals.size();
}
This function uses the data from the getRawableVoltage function and uses it the polynomial generated in the analog lasses to calculate the final voltage. Thus this was the core backend of calculating the voltages through channels in PSLab.
Resources:
-
-
- How to establish communication between android device and usb, coderanch.com:
https://coderanch.com/t/622740/establish-communication-android-device-usb - PSLab-android codes, Github:
https://github.com/fossasia/pslab-android
- How to establish communication between android device and usb, coderanch.com:
-