Android graphview and data plotting timing issue? - java

I am currently attempting to "fix" an Android application intended to control a device over BLE. The application was written entirely by someone else, and I currently know very little Java. So if there are glaring flaws please just point them out and I'll fix them. I won't be able to give the rationale very everything you see here, but I'll do my best. While there are a myriad of problems and things to fix, my current issue is the data plotter. GraphView was implemented to create the plots. The issue is that the data plotted is not representative of the actual sampling rate. When using a separate BLE data logger I can verify that the data is being sent at the correct rate and it plots out correctly using excel.
These are created on startup.
graph1 = (GraphView) findViewById(R.id.graph1);
airPressureSeries = new LineGraphSeries<>();
fluidPressureSeries = new LineGraphSeries<>();
airPressureSeries.setDrawAsPath(true);
fluidPressureSeries.setDrawAsPath(true);
graph1.getViewport().setXAxisBoundsManual(true);
graph1.getViewport().setMinX(0);
graph1Num = 100;
graph1.getViewport().setMaxX(graph1Num);
graph2 = (GraphView) findViewById(R.id.graph2);
airTemperatureSeries = new LineGraphSeries<>();
fluidTemperatureSeries = new LineGraphSeries<>();
airTemperatureSeries.setDrawDataPoints(true);
fluidTemperatureSeries.setDrawDataPoints(true);
graph2.getViewport().setXAxisBoundsManual(true);
graph2.getViewport().setMinX(0);
graph2Num = graph1Num;
graph2.getViewport().setMaxX(graph2Num);
Then this is done when data plotting is to be shown.
plot1Dropdown.setText("Air Pressure");
graph1.addSeries(airPressureSeries);
plot1Title.setText(R.string.pressureTitle);
plot2Dropdown.setText("Fluid Pressure");
plot2Title.setText(R.string.pressureTitle);
graph2.addSeries(fluidPressureSeries);
This timer, which I don't quite understand and could be wrong, is started. They said it should run at 100Hz. The code inside it should just increment an x value for the plot and then grab a global variable which is updated elsewhere.
mTimer1 = new Runnable() {
#Override
public void run() {
graph1LastXValue += 1d;
airPressureSeries.appendData(new DataPoint(graph1LastXValue,
airPressureValue), true, graph1Num);
fluidPressureSeries.appendData(new DataPoint(graph1LastXValue,
fluidPressureValue), true, graph1Num);
graph2LastXValue += 1d;
airTemperatureSeries.appendData(new DataPoint(graph2LastXValue,
airTemperatureValue), true, graph2Num);
fluidTemperatureSeries.appendData(new DataPoint(graph2LastXValue
, fluidTemperatureValue), true, graph2Num);
mHandler.postDelayed(this, 5);
}
};
mHandler.postDelayed(mTimer1, 5);
The global variable is updated after new data has been received and parsed by an AsyncTask. In the doInBackground of the AsyncTask it assigns the parsed values to the global variables like this.
DeviceControlActivity.airPressureValue = airPressure;
DeviceControlActivity.airTemperatureValue = airTemperature;
DeviceControlActivity.fluidPressureValue = fluidPressure;
DeviceControlActivity.fluidTemperatureValue = fluidTemperature;
I have done a few things to check what's going on. I am certain that the timer is running fast enough to update the values. I am also certain that data is being received at the correct rate. I used logging to make sure of that. I also am sure that the async task in running and completing in a reasonable amount of time. In the post execute for it I told it to log the amount of time passed between pre and post execute function calls. It was consistently 1 ms which is fine for the application.
I think the issue has to do with how the values are set or retrieved. Based on the way the code inside the timer works it plots a new point even if the data has not been updated so I get regions of the plot that are all the same value for some amount of time greater than is reasonable.
Can someone point me in the right direction?

Related

LeJOS GyroSensor sample always 0

I would like to read the current angle from my Ev3GyroSensor, however the returned value is always 0.
I tested multiple Ports nothing worked.
If I test the sensor with the LeJOS integrated Tool, everything works fine.
EV3GyroSensor ev3GyroSensor = new EV3GyroSensor(SensorPort.S4);
SampleProvider sampleProvider = ev3GyroSensor.getAngleAndRateMode();
float[] sample = new float[sampleProvider.sampleSize()];
sampleProvider.fetchSample(sample, 0);
System.out.println(sample[0]);
//I move the robot with my hand
Delay.msDelay(3000);
sampleProvider.fetchSample(sample, 0);
System.out.println(sample[0]);
ev3GyroSensor.close();
try using only angle mode, from my experience, angle and rate mode doesn't always work correctly.
There is also a possibility that you are reading the rate from the sample, but I'm not really sure.

can I control the refresh rate of a Java listener?

I'm struggling with this issue since some days ago and I'm not able to find a solution.
I have a listener which receives market data (orders at bid and ask). If market is quiet (pre-market or post-market (low volatility)) everything works fine. But once the market is open the listener receives events too fast. So after a couple of minutes my app freezes.
Right now the listener only assigns the received data to a var.
orderBookBid.getBuyOrders().addListener(new ObservableListModelListener<Order>() {
#Override
public void modelChanged(final Change<? extends Order> change) {
System.out.println("bid event");
bidChange = change.getSource();
}
});
The program only freezes when uses real data. When market is closed and uses test data from a local file works fine.
Is there any way to set the maximum number of events per second? Or any way to ignore events for a short time period?
Any idea on how can I handle this would be very appreciated.
Thanks.
You could put a load balancer in your application, that way it will create a queue and will not freeze the application.
If you want to let go some events, in the logic of your listener, you should have something that check if it's been X time since the last time you managed the event.
private long timeSinceLastEventManaged = 0;
private final static long MINIMUM_TIME = 2000; //2 seconds
In your listener
public void modelChanged(final Change<? extends Order> change) {
long timeSystem = System.currentTimeMillis();
if(timeSystem - timeSinceLastEventManaged > MINIMUM_TIME){
//do your stuff
timeSinceLastEventManaged = timeSystem;
}
}
First of all you should get rid of the println as it is really slow
The rest depends on what you are doing. Right now it seems that you are just getting the value and writing it to a variable. You will only see the latest change that way and if that is what you want the solution #TyMarc suggested will work fine.
If what you showed us is just an example and you really need every change things get a bit more complicated. Your modelChanged method should be changed to add the current value to a queue (e.g a LinkedList or Stack).
public void modelChanged(final Change<? extends Order> change)
{
syncronized(syncObject)
{
//add to your preffered queue
syncObject.notifyAll()
}
}
This frees your listener from the real work and it can keep collecting data.
I added a syncronized as someone has to do the work. For this you can use a Thread that runs something like this:
Order current;
while(keeprunning)
{
syncronized(syncObject)
{
if(queue.hasNext())
{
current = queue.getNext()
}
else
{
Thread.wait()
}
}
//do the real work here
}
Now someone else has the problem. Literally. If the Thread can't handle the inflow of data the queue will grow in size until you run out of memory or hit some other limit. But that's another story.
And yes, nothing of this will compile as I only wanted to show an example

Suggestions to load data faster

I'm seeking for solutions to load 20 items from SQLite faster than 5 seconds (which is the seconds that I'm loading right now.) - First of all, I'm using a custom Listview Adapter.
I load 5 items in 1 second. I tried to load 20 items and I load them in 5 seconds.
This are the fields I retrieve from database: int, String, String, int, Bytes, float, int.
As you may think, after getting the bytes I convert them into Bitmap.
Bitmap image = convertBlobToImage(cursor.getBlob(4));
// Using this function:
public Bitmap convertBlobToImage(byte[] value){
byte[] new_value = Base64.decode(value, 0);
return BitmapFactory.decodeByteArray(new_value, 0, new_value.length);
}
So, in my class fields, they are going to get not the bytes but already the bitmap.
One of the reasons of the amount of time to read, is probably the bitmap. I just did a test on Paint. I saved two equal images one in BMP and another in JPG. The JPG image have the size of 2,87KB and the BMP 420KB!!
With the code above, is that result I'm getting? And probably one of the solutions could be: http://www.mkyong.com/java/how-to-convert-byte-to-bufferedimage-in-java/ ?
What do you guys think? Thanks.
Edit:
I was searching and I found about onDestroy(). Also I didn't have the "runOnUiThread" implemented, and I put that way. But I think it didn't give me any better result. What do you think? Could this increase the performance?
#Override
protected void onDestroy() {
super.onDestroy();
listView.setAdapter(null);
}
// And I also tried to put runOnUiThread:
runOnUiThread(new Runnable() {
#Override
public void run() {
Bundle extras = getIntent().getExtras();
if (extras != null) {
DatabaseHandler db = new DatabaseHandler(Produtos.this);
display_products = db.get_all_oc_product(extras.getString("category_id"));
listView = (ListView) findViewById(R.id.product_listview);
inputSearch = (EditText) findViewById(R.id.product_inputSearch);
adapter = new itemAdapter(Produtos.this,R.layout.row, display_products);
listView.setAdapter(adapter);
}
}
});
Edit (2): I managed to decrease the time for 3 seconds on displaying 20 items. I accomplish this by closing all the connections to database after the queries. I was not doing this properly.
Correct way:
cursor db.query(...)
try{
// Code
} finally {
cursor.close();
db.close();
}
Edit (3): Further than the solution on Edit (2), one of the issues I had, which I was identified with, was the problem of the images.
So, I started to look at them and I saw images the 2000x1800 and 300kb and even more, and I found rapidly that was here the problem.
So, in the PHP webservice I developed a function to resize the images to half and converting them to jpg.
function resize($filePath){
list($width, $height) = getimagesize($filePath);
$percent = 0.5;
$newwidth = $width * $percent;
$newheight = $height * $percent;
$thumb = imagecreatetruecolor($newwidth, $newheight);
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
$source = null;
if($ext == "png"){
$source = imagecreatefrompng($filePath);
}else if($ext == "jpg" || $ext == "jpeg"){
$source = imagecreatefromjpeg($filePath);
}
// Resize
imagecopyresized($thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
// Output
$temporary = "C:\\xampp\\htdocs\\MyExample\\images\\temporary\\" . basename($filePath);
imagejpeg($thumb, $temporary . ".jpg", 50);
ImageDestroy($thumb);
return $temporary . ".jpg";
}
With this solution, I decreased the time for a stunning 1 second loading 47 items!!
I'd recommend checking into one of the java.nio.* packages to load your images. NIO is non-blocking which means you can load images asynchronously, resulting in more work done per time.
NIO also used native buffers on the system (outside the jvm) so it can prevent java from having to read things into it's heap, which again, makes things faster (native). There's a lot more benefits from using NIO here as well.
Well It's a bit hard to help from the other side of this question, but some generic tips:
Use profiling of some sort to determine which part is slowing you down. Is it loading the actual cursor back that's a problem? or is it the byte conversion? It's hard to say unless you measure - otherwise you could waste a lot of time optimizing something that isn't slowing you down. The simplest profiling example:
long startTime = SystemClock.elapsedRealTime();
suspectedSlowFunctionCall();
long endTime = SystemClock.elapsedRealTime();
Log.d(TAG, "Method duration: " + (endTime - startTime));
As Mr. Murphy mentioned in the comments, there are also tools that help you with this task. The android developers site has a great post showing how to use traceview, which is included in the SDK.
General wisdom is to not store Images in the database if you can avoid it. It is a lot easier on you to debug and work with (and for the system to load) if you store a file path instead and save the file.
Android's BitmapFactory should be able to handle JPEGs. If you want to use JPG, feel free
If something is unfixably slow, don't do it on the UI Thread! use something like an AsyncTask to do the slow operation, and show your results to the user one by one as they get loaded. This is always preferable to an operation that stalls whilst the user waits confused.

LWUIT: Load images in background thread

I have a list that contains about 20 image URLs and some other things.
I want to display the other things (description) and allow the user to interact with the app while I load the 20 images.
What I noticed is that no matter what I tried, I can't interact with the form until the images finished loading even though I am doing the loading in another thread.
This is my solution I am using now.
private Container createServerItems() throws Exception {
Container list = new Container(new BoxLayout(BoxLayout.Y_AXIS));
final int size = mediaList.size();
final Button buttons[] = new Button[size];
System.out.println("In here: " + size);
for (int i = 0; i < size; i++) {
Container mainContainer = new Container(new BorderLayout());
Media m = new Media();
m.fromJSONString(mediaList.elementAt(i).toString());
buttons[i] = new Button("please wait");
final int whichButton = i;
Display.getInstance().callSerially(new Runnable() {
public void run() {
try {
System.out.println(MStrings.replaceAll(m.getImgURL(), "\"", ""));
final StreamConnection streamConnection = (StreamConnection) Connector.open(MStrings.replaceAll(m.getImgURL(), "\"", ""));
Image image = Image.createImage(streamConnection.openInputStream());
streamConnection.close();
buttons[whichButton].setText("");
buttons[whichButton].setIcon(image.scaled(32, 32));
} catch (Exception e) {
}
}
});
TextArea t = new TextArea(m.getDesc());
t.setEditable(false);
t.setFocusable(false);
t.setGrowByContent(true);
mainContainer.addComponent(BorderLayout.WEST, buttons[i]);
mainContainer.addComponent(BorderLayout.CENTER, t);
list.addComponent(mainContainer);
}
return list;
}
APPROACH I : LWUIT 1.5 has a powerful LWUIT4IO library to address your problem.
An excerpt from Shai's Blog link
A feature in LWUIT4IO to which I didn't give enough spotlight is the
cache map, its effectively a lean hashtable which stores its data
using weak/soft references (depending on the platform) and falls back
to storage when not enough memory is available. Its a great way to
cache data without going overboard. One of the cool things about it is
the fact that we use it seamlessly for our storage abstraction (which
hides RMS or equivalent services) in effect providing faster access to
RMS storage which is often slow on devices.
Another useful link is here
The idea is to delegate the Network IO functionality to a singleton to avoid any UI deadlocks, like the one you are facing.
A very good video demo here by vprise, explains how to bind GUI functionality to your netbeans. In this video at around 7:00 mins it explains the use of ImageDownloadService class which binds the component to its thumbnail url which will seamlessly fetch from the network and populate the Image.
APPROACH II: Difficult one of create custom logic
Create a singleton that will interface with the network to fetch the
data
Use a queue to handle the sequential image download services
Create a new thread for this singleton and wait on the queue.
With each image download service bind a listener with the invoking
component so that it easier to update the right component.
According to the lwuit spec, callSerially() executes on the Event Dispatch Thread, which means that it will block other events until it completes. You need to move your code to load the image outside of that method and keep only the setText and setIcon calls in callSerially().

Update photo in flex without blinking

i am trying to simulate a live view using a canon Camera.
I am interacting with the cam using the CanonSDK, i get an image every a short period in order to simulate a video frame by frame. This works fine, i am using java to do the backend and send the images trough BlazeDS to flex.
The problem is not getting the image, the problem is that when i load a new image using something like:
image.source=my_new_image;
the new image is loaded but it produces a short white blink and it ruins the video...
So i would like to know if the is a way to update an image on flex avoiding the blinking problem, or if i could make a video streaming from java and pick it up with flex...
Thanks in advance!!!
The easy way is to use a technique called double buffering, using two Loaders - one for the image which is visible, and one for the image which is being loaded and is invisible. When the image has completed loading it becomes visible, and the other one becomes invisible and the process repeats.
In terms of efficiency, it would be better to at least use a socket connection to the server for transferring the image bytes, preferably in AMF format since it has little overhead. This is all fairly possible in BlazeDS with some scripting.
For better efficiency you may try using a real-time frame or video encoder on the server, however decoding the video on the client will be challenging. For best performance it will be better to use the built-in video decoder and a streaming server such as Flash Media Server.
UPDATE (example script):
This example loads images over HTTP. A more efficient approach would be to use an AMF socket (mentioned above) to transfer the image, then use Loader.loadBytes() to display it.
private var loaderA:Loader;
private var loaderB:Loader;
private var foregroundLoader:Loader;
private var backgroundLoader:Loader;
public function Main()
{
loaderA = new Loader();
loaderB = new Loader();
foregroundLoader = loaderA;
backgroundLoader = loaderB;
loadNext();
}
private function loadNext():void
{
trace("loading");
backgroundLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteHandler);
backgroundLoader.load(new URLRequest("http://www.phpjunkyard.com/randim/randim.php?type=1"));
}
private function loaderCompleteHandler(event:Event):void
{
trace("loaded");
var loaderInfo:LoaderInfo = event.target as LoaderInfo;
var loader:Loader = loaderInfo.loader;
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaderCompleteHandler);
if (contains(foregroundLoader))
removeChild(foregroundLoader);
var temp:Loader = foregroundLoader;
foregroundLoader = backgroundLoader;
backgroundLoader = temp;
addChild(foregroundLoader);
loadNext();
}

Categories

Resources