TK06a GPS Tracker Protocol : Long/Lat Format - gps

I have a GPS tracker with TK06a Chipset, and I have my own tcp listener, everything is working fine, I have received the data from the device with this format :
#355488020131775##1#0000#AUT#01#52500100232a47#10341.175280,E,121.322800,N,0.28,0.00#111113#171607.000##
I think i figured out what are these, (for example the first one is the IMEI), but I didn't know how to convert (10341.175280,E) and (121.322800,N) to something that google maps can understand.
beside the device has a poor user manual and no documentation for the protocol.
the real location should be in here (1.355269,103.686426) maybe this can lead you to solve this mystery :)
Thanks in advance.
Edit:
I Found this on the web, maybe some will find it useful :
The decode of the line above.
the IMEI number cannot be empty, if the SIM card number regarded as device series number, then the data of IMEI part should be filled in
SIM cad number.
SIM card number: this part can be empty , or also can be same as 1st point , fill in SIM card number.
0 or 1 , reserve (original meaning is ACC status )
Device password ( 0-9 numbers, digit cannot over 6 digits, generally is in 4 digits )
Reserved word AUT, cannot be changed .
Numbers of data, 00-99 , in 2 digits.
The format of Each data as below:
#base station number#Longitude, East and West identification, latitude,North and South identification,speed(nm), direction angle(0-360)#date#time
Base station number can be empty.
Longitude, format : dddff.ffff, the degree part must be in 3 integer, the minute part must be in 2 integer, the decimal part is in
4 digits, there is no separator between degree and minute.
East and West identification, only one character , E/W.
Latitude, format : ddff.ffff, same as Longitude , only the degree part is in 2 integer.
North and South identification, only one character , N/S.
Speed: can be 0.
Direction : can be 0.
Date, format : ddmmyy.
Time, format: hhnnss.mmm, the part before decimal point should be hour, minute and second in turn, each is in 2 digits, the part after
decimal point should be milliseconds, it can be 000.

This format is DM like in NMEA RMC message, but with a missing leading 0:
given longitude: 10341.175280 E
The first 3 digits are degrees: 103
Then the rest is minutes: 41.175280
This now is fomrat "DM" Degrees and decimal minutes.
Google uses "DEG" (Decimal degrees)
convert: 103 + 41.175280 / 60.0 = 103.686254
(DEG = degrees + minutes / 60.0)
wich fits perfectly to your location
Now it is a bit strange:
It should read "0121.322800" not "121.322800"
But then similar to above but since latitude is limited to two digits:
The first 2 digits are always degrees: 01
Then the rest is minutes: 21.322800
same formala as above: lat= 1 + 21.322800 / 60.0 = 1,35538
finally: if W or S, multiply the deg value with -1
(In your case it is N and E, so it stays as it is - positive)
This format looks partly like the NMEA RMC sentence

I think you want to make this work with OpenGTS.
So here what i done to work:(Note i dont need the tk10x devices so i overwrited the files, you can create another class if you want)
go to $GTS_HOME/src/org/opengts/servers/tk10x
and change the TrackServer.java with this code
I writed a new parseInsertFunction
package org.opengts.servers.tk10x;
import java.lang.*;
import java.util.*;
import java.io.*;
import java.net.*;
import java.sql.*;
import org.opengts.util.*;
import org.opengts.db.*;
import org.opengts.db.tables.*;
public class TrackServer
{
// ------------------------------------------------------------------------
// initialize runtime configuration
public static void configInit()
{
DCServerConfig dcs = Main.getServerConfig();
if (dcs != null) {
TrackServer.setTcpIdleTimeout( dcs.getTcpIdleTimeoutMS( Constants.TIMEOUT_TCP_IDLE ));
TrackServer.setTcpPacketTimeout( dcs.getTcpPacketTimeoutMS( Constants.TIMEOUT_TCP_PACKET ));
TrackServer.setTcpSessionTimeout(dcs.getTcpSessionTimeoutMS(Constants.TIMEOUT_TCP_SESSION));
TrackServer.setUdpIdleTimeout( dcs.getUdpIdleTimeoutMS( Constants.TIMEOUT_UDP_IDLE ));
TrackServer.setUdpPacketTimeout( dcs.getUdpPacketTimeoutMS( Constants.TIMEOUT_UDP_PACKET ));
TrackServer.setUdpSessionTimeout(dcs.getUdpSessionTimeoutMS(Constants.TIMEOUT_UDP_SESSION));
}
}
// ------------------------------------------------------------------------
// Start TrackServer (TrackServer is a singleton)
private static TrackServer trackServerInstance = null;
/* start TrackServer on array of ports */
public static TrackServer startTrackServer(int tcpPorts[], int udpPorts[], int commandPort)
throws Throwable
{
if (trackServerInstance == null) {
trackServerInstance = new TrackServer(tcpPorts, udpPorts, commandPort);
} else {
//Print.logError("TrackServer already initialized!");
}
return trackServerInstance;
}
public static TrackServer getTrackServer()
{
return trackServerInstance;
}
// ------------------------------------------------------------------------
// TCP Session timeouts
/* idle timeout */
private static long tcpTimeout_idle = Constants.TIMEOUT_TCP_IDLE;
public static void setTcpIdleTimeout(long timeout)
{
TrackServer.tcpTimeout_idle = timeout;
}
public static long getTcpIdleTimeout()
{
return TrackServer.tcpTimeout_idle;
}
/* inter-packet timeout */
private static long tcpTimeout_packet = Constants.TIMEOUT_TCP_PACKET;
public static void setTcpPacketTimeout(long timeout)
{
TrackServer.tcpTimeout_packet = timeout;
}
public static long getTcpPacketTimeout()
{
return TrackServer.tcpTimeout_packet;
}
/* total session timeout */
private static long tcpTimeout_session = Constants.TIMEOUT_TCP_SESSION;
public static void setTcpSessionTimeout(long timeout)
{
TrackServer.tcpTimeout_session = timeout;
}
public static long getTcpSessionTimeout()
{
return TrackServer.tcpTimeout_session;
}
// ------------------------------------------------------------------------
// UDP Session timeouts
/* idle timeout */
private static long udpTimeout_idle = Constants.TIMEOUT_UDP_IDLE;
public static void setUdpIdleTimeout(long timeout)
{
TrackServer.udpTimeout_idle = timeout;
}
public static long getUdpIdleTimeout()
{
return TrackServer.udpTimeout_idle;
}
/* inter-packet timeout */
private static long udpTimeout_packet = Constants.TIMEOUT_UDP_PACKET;
public static void setUdpPacketTimeout(long timeout)
{
TrackServer.udpTimeout_packet = timeout;
}
public static long getUdpPacketTimeout()
{
return TrackServer.udpTimeout_packet;
}
/* total session timeout */
private static long udpTimeout_session = Constants.TIMEOUT_UDP_SESSION;
public static void setUdpSessionTimeout(long timeout)
{
TrackServer.udpTimeout_session = timeout;
}
public static long getUdpSessionTimeout()
{
return TrackServer.udpTimeout_session;
}
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// TCP port listener threads
private java.util.List<ServerSocketThread> tcpThread = new Vector<ServerSocketThread>();
// UDP port listener threads
private java.util.List<ServerSocketThread> udpThread = new Vector<ServerSocketThread>();
// Command port listener thread
private ServerSocketThread cmdThread = null;
private DatagramSocket udpSocket = null;
// ------------------------------------------------------------------------
/* private constructor */
private TrackServer(int tcpPorts[], int udpPorts[], int commandPort)
throws Throwable
{
int listeners = 0;
// Start TCP listeners
if (!ListTools.isEmpty(tcpPorts)) {
for (int i = 0; i < tcpPorts.length; i++) {
int port = tcpPorts[i];
if (ServerSocketThread.isValidPort(port)) {
try {
this._startTCP(port);
listeners++;
} catch (java.net.BindException be) {
Print.logError("TCP: Error binding to port: %d", port);
}
} else {
throw new Exception("TCP: Invalid port number: " + port);
}
}
}
// Start UDP listeners
if (!ListTools.isEmpty(udpPorts)) {
for (int i = 0; i < udpPorts.length; i++) {
int port = udpPorts[i];
if (ServerSocketThread.isValidPort(port)) {
try {
ServerSocketThread sst = this._startUDP(port);
if (this.udpSocket == null) {
this.udpSocket = sst.getDatagramSocket();
}
listeners++;
} catch (java.net.BindException be) {
Print.logError("UDP: Error binding to port: %d", port);
}
} else {
throw new Exception("UDP: Invalid port number: " + port);
}
}
}
/* do we have any active listeners? */
if (listeners <= 0) {
Print.logWarn("No active device communication listeners!");
}
}
// ------------------------------------------------------------------------
/* start TCP listener */
private void _startTCP(int port)
throws Throwable
{
ServerSocketThread sst = null;
/* create server socket */
try {
sst = new ServerSocketThread(port);
} catch (Throwable t) { // trap any server exception
Print.logException("ServerSocket error", t);
throw t;
}
/* initialize */
sst.setTextPackets(Constants.ASCII_PACKETS);
sst.setBackspaceChar(null); // no backspaces allowed
sst.setLineTerminatorChar(Constants.ASCII_LINE_TERMINATOR);
sst.setIgnoreChar(Constants.ASCII_IGNORE_CHARS);
sst.setMaximumPacketLength(Constants.MAX_PACKET_LENGTH);
sst.setMinimumPacketLength(Constants.MIN_PACKET_LENGTH);
sst.setIdleTimeout(TrackServer.tcpTimeout_idle); // time between packets
sst.setPacketTimeout(TrackServer.tcpTimeout_packet); // time from start of packet to packet completion
sst.setSessionTimeout(TrackServer.tcpTimeout_session); // time for entire session
sst.setTerminateOnTimeout(Constants.TERMINATE_ON_TIMEOUT);
sst.setClientPacketHandlerClass(TrackClientPacketHandler.class);
sst.setLingerTimeoutSec(Constants.LINGER_ON_CLOSE_SEC);
/* start thread */
Print.logInfo("Starting TCP listener thread on port " + port + " [timeout=" + sst.getSessionTimeout() + "ms] ...");
sst.start();
this.tcpThread.add(sst);
}
// ------------------------------------------------------------------------
/* start UDP listener */
private ServerSocketThread _startUDP(int port)
throws Throwable
{
ServerSocketThread sst = null;
/* create server socket */
try {
sst = new ServerSocketThread(ServerSocketThread.createDatagramSocket(port));
} catch (Throwable t) { // trap any server exception
Print.logException("ServerSocket error", t);
throw t;
}
/* initialize */
sst.setTextPackets(Constants.ASCII_PACKETS);
sst.setBackspaceChar(null); // no backspaces allowed
sst.setLineTerminatorChar(Constants.ASCII_LINE_TERMINATOR);
sst.setIgnoreChar(Constants.ASCII_IGNORE_CHARS);
sst.setMaximumPacketLength(Constants.MAX_PACKET_LENGTH);
sst.setMinimumPacketLength(Constants.MIN_PACKET_LENGTH);
sst.setIdleTimeout(TrackServer.udpTimeout_idle);
sst.setPacketTimeout(TrackServer.udpTimeout_packet);
sst.setSessionTimeout(TrackServer.udpTimeout_session);
sst.setTerminateOnTimeout(Constants.TERMINATE_ON_TIMEOUT);
sst.setClientPacketHandlerClass(TrackClientPacketHandler.class);
/* start thread */
Print.logInfo("Starting UDP listener thread on port " + port + " [timeout=" + sst.getSessionTimeout() + "ms] ...");
sst.start();
this.udpThread.add(sst);
return sst;
}
public DatagramSocket getUdpDatagramSocket()
{
return this.udpSocket;
}
// ------------------------------------------------------------------------
}`
and in Constant.java find 'ASCII_LINE_TERMINATOR[] ' constant declaration and add '000' to with
public static final int ASCII_LINE_TERMINATOR[] = new int[] {
// this list has been construction by observation of various data packets
0x00, 0xFF, 0xCE, '\0', '\n', '\r', ')', ';',000
};
after this
cd $GTS_HOME
ant tk10x
bin/runserver.sh -s tk10x
This should do the trick
And here is a link to the package i created
https://anonfiles.com/file/0aae22ccb3822618fb693cd667283b18

Related

ESP32 Multitask Parameter pass through Spreadsheet

I am working for soil moisture and pressure sensor using the same baud-rate 115200 in ESP32. Plus, I execute both sensor using multitask ESP32 with 2 core. Core 1 and Core 2 for both program.
The parameter can be viewed through serial monitor however it can’t be passed through the spreadsheet. I am using IFTTT platform to connect the ESP32 into Spreadsheet.
Here my code :
#include <WiFi.h>
#include <HTTPClient.h>
TaskHandle_t Task1;
TaskHandle_t Task2;
const char * ssid = "XXXX";
const char * password = "XXXX";
String server = "http://maker.ifttt.com";
String eventName = "soil_pressure";
String IFTTT_Key = "XXXXXXXXXX";
String IFTTTUrl="https://maker.ifttt.com/trigger/soil_pressure/with/key/XXXXX";
int sensorPin = 2;
int sensorValueSoil;
int limit = 300;
int sensorValuePressure;
int value1; // soil
int value2; // pressure
void setup()
{
Serial.begin(115200);
pinMode(2, OUTPUT);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("Internet Connected !!!");
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
delay(500);
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
delay(500);
}
void Task1code( void * pvParameters )
{
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
for(;;)
{
sensorValueSoil = analogRead(sensorPin);
Serial.println(sensorValueSoil);
if (sensorValueSoil<limit)
{
digitalWrite(2, HIGH);
}
else {
digitalWrite(2, LOW);
}
delay(1000);
}
}
void Task2code( void * pvParameters )
{
Serial.print("Task2 running on core ");
Serial.println(xPortGetCoreID());
for(;;)
{
sensorValuePressure = analogRead(13);
Serial.println(sensorValuePressure);
delay(1000);
}
}
void sendDataToSheet(void)
{
String url = server + "/trigger/" + eventName + "/with/key/" + IFTTT_Key + "
value1=" + String((int)value1) + "&value2="+String((int)value2);
Serial.println(url);
//Start to send data to IFTTT
HTTPClient http;
Serial.print("[HTTP] begin...\n");
http.begin(url); //HTTP
Serial.print("[HTTP] GET...\n");
// start connection and send HTTP header
int httpCode = http.GET();
// httpCode will be negative on error
if(httpCode > 0)
{
// HTTP header has been send and Server
response header has been handled
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
// file found at server
if(httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
}
}
else
{
Serial.printf("[HTTP] GET... failed, error: %s\n",
http.errorToString(httpCode).c_str());
}
http.end();
}
void loop()
{
value1 = sensorValueSoil;
value2 = sensorValuePressure;
Serial.print("Values are ");
Serial.print(value1);
Serial.print(' ');
Serial.print(value2);
Serial.print(' ');
sendDataToSheet();
delay(5000);
}
You need to pass WiFiClient object into HTTPClient arguments.
HTTPClient http;
WiFiClient client;
void sendDataToSheet(void) {
String url = server + "/trigger/" + eventName + "/with/key/" + IFTTT_Key + "value1 = " + String((int)value1) + " & value2 = " + String((int)value2);
Serial.println(url);
//Start to send data to IFTTT
Serial.print("[HTTP] begin...\n");
http.begin(client, url);
// start connection and send HTTP header
int httpCode = http.GET();
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been send and Server response header has been handled
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}

Optimizing Transposing and Comparison of Concurrent List in Java 8

I have an application in Java 8 Collecting Data of multiple Threads using BlockingQueue.
I need to perform comparison of samples.
But my application is very large, I implemented a mock application (Github) in Java 8.
I'm generating a chunk of bytes (really is random order).
The bytes are stored into ChunkDTO class.
I implemented a capturer the ChunkDTO in a List, code in Capturer class.
Each ChunkDTO of List is translated into a List of Samples (TimePitchValue exactly) returning a nested List (or List of List of TimePitchValue).
Later the nested List is transposed in order to performs comparisons between TimePitchValue with the same time value.
Due to enormous volume of TimePitchValue instances it's consumes huge time in my application.
Here some code (The complete functional Code is in Github) because is still large for this site).
public class Generator {
final static Logger LOGGER = Logger.getLogger("SampleComparator");
public static void main(String[] args) {
long previous = System.nanoTime();
final int minBufferSize = 2048;
int sampleRate = 8192;
int numChannels = 1;
int numBytesPerSample = 1;
int samplesChunkPerSecond = sampleRate / minBufferSize;
int minutes = 0;
int seconds = 10;
int time = 60 * minutes + seconds;
int chunksBySecond = samplesChunkPerSecond * numBytesPerSample * numChannels;
int pitchs = 32;
boolean signed = false;
boolean endianness = false;
AudioFormat audioformat = new AudioFormat(sampleRate, 8 * numBytesPerSample, numChannels, signed, endianness);
ControlDSP controlDSP = new ControlDSP(audioformat);
BlockingQueue<ChunkDTO> generatorBlockingQueue = new LinkedBlockingQueue<>();
Capturer capturer = new Capturer(controlDSP, pitchs, pitchs * time * chunksBySecond, generatorBlockingQueue);
controlDSP.getListFuture().add(controlDSP.getExecutorService().submit(capturer));
for (int i = 0; i < time * chunksBySecond; i++) {
for (int p = 0; p < pitchs; p++) {
ChunkDTO chunkDTO = new ChunkDTO(UtilClass.getArrayByte(minBufferSize), i, p);
LOGGER.info(String.format("chunkDTO: %s", chunkDTO));
try {
generatorBlockingQueue.put(chunkDTO);
} catch (InterruptedException ex) {
LOGGER.info(ex.getMessage());
}
}
try {
Thread.sleep(1000 / chunksBySecond);
} catch (Exception ex) {
}
}
controlDSP.tryFinishThreads(Thread.currentThread());
long current = System.nanoTime();
long interval = TimeUnit.NANOSECONDS.toSeconds(current - previous);
System.out.println("Seconds Interval: " + interval);
}
}
Capturer Class
public class Capturer implements Callable<Void> {
private final ControlDSP controlDSP;
private final int pitchs;
private final int totalChunks;
private final BlockingQueue<ChunkDTO> capturerBlockingQueue;
private final Counter intCounter;
private final Map<Long, List<ChunkDTO>> mapIndexListChunkDTO = Collections.synchronizedMap(new HashMap<>());
private volatile boolean isRunning = false;
private final String threadName;
private static final Logger LOGGER = Logger.getLogger("SampleComparator");
public Capturer(ControlDSP controlDSP, int pitchs, int totalChunks, BlockingQueue<ChunkDTO> capturerBlockingQueue) {
this.controlDSP = controlDSP;
this.pitchs = pitchs;
this.totalChunks = totalChunks;
this.capturerBlockingQueue = capturerBlockingQueue;
this.intCounter = new Counter();
this.controlDSP.getListFuture().add(this.controlDSP.getExecutorService().submit(() -> {
while (intCounter.getValue() < totalChunks) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
}
capturerBlockingQueue.add(new ChunkDTOStopper());
}));
this.threadName = this.getClass().getSimpleName();
}
#Override
public Void call() throws Exception {
long quantity = 0;
isRunning = true;
while (isRunning) {
try {
ChunkDTO chunkDTO = capturerBlockingQueue.take();
if (chunkDTO instanceof ChunkDTOStopper) {
break;
}
//Find or Create List (according to Index) to add the incoming Chunk
long index = chunkDTO.getIndex();
int sizeChunk = chunkDTO.getChunk().length;
List<ChunkDTO> listChunkDTOWithIndex = getListChunkDTOByIndex(chunkDTO);
//When the List (according to Index) is completed and processed
if (listChunkDTOWithIndex.size() == pitchs) {
mapIndexListChunkDTO.remove(index);
TransposerComparator transposerComparator = new TransposerComparator(controlDSP, controlDSP.getAudioformat(), index, sizeChunk, listChunkDTOWithIndex);
controlDSP.getListFuture().add(controlDSP.getExecutorService().submit(transposerComparator));
}
quantity++;
intCounter.setValue(quantity);
LOGGER.info(String.format("%s\tConsumes:%s\ttotal:%05d", threadName, chunkDTO, quantity));
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
}
LOGGER.info(String.format("%s\tReceived:%05d\tQty:%s\tPitchs:%s\tEND\n", threadName, quantity, quantity / pitchs, pitchs));
return null;
}
private List<ChunkDTO> getListChunkDTOByIndex(ChunkDTO chunkDTO) {
List<ChunkDTO> listChunkDTOWithIndex = mapIndexListChunkDTO.get(chunkDTO.getIndex());
if (listChunkDTOWithIndex == null) {
listChunkDTOWithIndex = new ArrayList<>();
mapIndexListChunkDTO.put(chunkDTO.getIndex(), listChunkDTOWithIndex);
listChunkDTOWithIndex = mapIndexListChunkDTO.get(chunkDTO.getIndex());
}
listChunkDTOWithIndex.add(chunkDTO);
return listChunkDTOWithIndex;
}
}
TransposerComparator class.
The optimization required is in this code, specifically on transposedNestedList method.
public class TransposerComparator implements Callable<Void> {
private final ControlDSP controlDSP;
private final AudioFormat audioformat;
private final long index;
private final int sizeChunk;
private final List<ChunkDTO> listChunkDTOWithIndex;
private final String threadName;
private static final Logger LOGGER = Logger.getLogger("SampleComparator");
public TransposerComparator(ControlDSP controlDSP, AudioFormat audioformat, long index, int sizeChunk, List<ChunkDTO> listChunkDTOWithIndex) {
this.controlDSP = controlDSP;
this.audioformat = audioformat;
this.index = index;
this.sizeChunk = sizeChunk;
this.listChunkDTOWithIndex = listChunkDTOWithIndex;
this.threadName = this.getClass().getSimpleName() + "_" + String.format("%05d", index);
}
#Override
public Void call() throws Exception {
Thread.currentThread().setName(threadName);
LOGGER.info(String.format("%s\tINI", threadName));
try {
int numBytesPerSample = audioformat.getSampleSizeInBits() / 8;
int quantitySamples = sizeChunk / numBytesPerSample;
long baseTime = quantitySamples * index;
// Convert the List of Chunk Bytes to Nested List of TimePitchValue
List<List<TimePitchValue>> nestedListTimePitchValue = listChunkDTOWithIndex
.stream()
.map(chunkDTO -> {
return IntStream
.range(0, quantitySamples)
.mapToObj(time -> {
int value = extractValue(chunkDTO.getChunk(), numBytesPerSample, time);
return new TimePitchValue(chunkDTO.getPitch(), baseTime + time, value);
}).collect(Collectors.toList());
}).collect(Collectors.toList());
List<List<TimePitchValue>> timeNestedListTimePitchValue = transposedNestedList(nestedListTimePitchValue);
} catch (Exception ex) {
ex.printStackTrace();
LOGGER.log(Level.SEVERE, null, ex);
throw ex;
}
return null;
}
private static int extractValue(byte[] bytesSamples, int numBytesPerSample, int time) {
byte[] bytesSingleNumber = Arrays.copyOfRange(bytesSamples, time * numBytesPerSample, (time + 1) * numBytesPerSample);
int value = numBytesPerSample == 2
? (UtilClass.Byte2IntLit(bytesSingleNumber[0], bytesSingleNumber[1]))
: (UtilClass.byte2intSmpl(bytesSingleNumber[0]));
return value;
}
private static List<List<TimePitchValue>> transposedNestedList(List<List<TimePitchValue>> nestedList) {
List<List<TimePitchValue>> outNestedList = new ArrayList<>();
nestedList.forEach(pitchList -> {
pitchList.forEach(pitchValue -> {
List<TimePitchValue> listTimePitchValueWithTime = listTimePitchValueWithTime(outNestedList, pitchValue.getTime());
if (!outNestedList.contains(listTimePitchValueWithTime)) {
outNestedList.add(listTimePitchValueWithTime);
}
listTimePitchValueWithTime.add(pitchValue);
});
});
outNestedList.forEach(pitchList -> {
pitchList.sort(Comparator.comparingInt(TimePitchValue::getValue).reversed());
});
return outNestedList;
}
private static List<TimePitchValue> listTimePitchValueWithTime(List<List<TimePitchValue>> nestedList, long time) {
List<TimePitchValue> listTimePitchValueWithTime = nestedList
.stream()
.filter(innerList -> innerList.stream()
.anyMatch(timePitchValue -> timePitchValue.getTime() == time))
.findAny()
.orElseGet(ArrayList::new);
return listTimePitchValueWithTime;
}
}
I was testing:
With 5 Seconds in Generator class and the List<List<TimePitchValue>> timeNestedListTimePitchValue = transposedNestedList(nestedListTimePitchValue); line in TransposerComparator class, Commented 7 Seconds needed, Uncommented 211 Seconds needed.
With 10 Seconds in Generator class and the List<List<TimePitchValue>> timeNestedListTimePitchValue = transposedNestedList(nestedListTimePitchValue); line in TransposerComparator class, Commented 12 Seconds needed, Uncommented 574 Seconds needed.
I need to use the application at least 60 minutes.
With the purpose of reduce the needed (consumed) time, I have two ways:
I choose for short is to optimize the methods that I am currently using.
That should be successful but longer and is to use GPGPU, but I don't know where to start implementing it yet.
QUESTIONS
This Question is for the first way: What changes do you recommend in the code of the transposedNestedList method in order to improve speed?
Is there better alternative to use this Comparison?
outNestedList.forEach(pitchList -> {
pitchList.sort(Comparator.comparingInt(TimePitchValue::getValue).reversed());
});

NAudio - ASIO Playback to device (static only)

I'm trying to route ASIO audio to my playback devices, however, all I hear is static.
ASIO Setup
BIT_PER_SAMPLE = 24
SAMPLE_RATE = 48000
Currently, trying with 1 channel into 1 playback device called "Line 1" for testing. The playback of the sound is static.
EDIT: The code below has been updated. It will take 64 channels of ASIO input and route them one at a time into Waveout devices (I'm using Virtual Audio Cable to create them)
private static AsioOut asioOut;
private static AsioInputPatcher inputPatcher;
private static readonly int BIT_PER_SAMPLE = 16;
private static readonly int SAMPLE_RATE = 48000;
private static readonly int NUMBER_OF_CHANNELS = 64;
private static BufferedWaveProvider[] bufferedWaveProviders = new BufferedWaveProvider[NUMBER_OF_CHANNELS];
private static WaveOut[] waveouts = new WaveOut[NUMBER_OF_CHANNELS];
[STAThread]
static void Main(string[] args)
{
InitDevices();
Record();
while (true)
{
Console.WriteLine("Recording...press any key to Exit.");
Console.ReadKey(true);
break;
}
asioOut.Stop();
asioOut.Dispose();
asioOut = null;
}
private static void Record()
{
inputPatcher = new AsioInputPatcher(SAMPLE_RATE, NUMBER_OF_CHANNELS, NUMBER_OF_CHANNELS);
asioOut = new AsioOut(AsioOut.GetDriverNames()[0]);
asioOut.InitRecordAndPlayback(new SampleToWaveProvider(inputPatcher),
NUMBER_OF_CHANNELS, 0);
asioOut.AudioAvailable += OnAsioOutAudioAvailable;
asioOut.Play();
}
private static void InitDevices()
{
for (int n = -1; n < WaveOut.DeviceCount; n++)
{
WaveOutCapabilities caps = WaveOut.GetCapabilities(n);
if (caps.ProductName.StartsWith("Line"))
{
int _number = int.Parse(caps.ProductName.Split(' ')[1]);
if (_number <= NUMBER_OF_CHANNELS)
{
waveouts[_number - 1] = new WaveOut() { DeviceNumber = n };
bufferedWaveProviders[_number - 1] = new BufferedWaveProvider(new WaveFormat(SAMPLE_RATE, BIT_PER_SAMPLE, 2));
waveouts[_number - 1].Init(bufferedWaveProviders[_number - 1]);
waveouts[_number - 1].Play();
}
}
}
}
static void OnAsioOutAudioAvailable(object sender, AsioAudioAvailableEventArgs e)
{
inputPatcher.ProcessBuffer(e.InputBuffers, e.OutputBuffers,
e.SamplesPerBuffer, e.AsioSampleType);
for (int outputChannel = 0; outputChannel < e.OutputBuffers.Length; outputChannel++)
{
byte[] buf = new byte[e.SamplesPerBuffer * (BIT_PER_SAMPLE / 8)];
Marshal.Copy(e.OutputBuffers[outputChannel], buf, 0, e.SamplesPerBuffer * (BIT_PER_SAMPLE / 8));
bufferedWaveProviders[outputChannel].AddSamples(buf, 0, buf.Length);
}
e.WrittenToOutputBuffers = true;
}
There are two main problems with your code. First, InputBuffers is one per channel, not sample. Second, when you set e.WrittenToOutputBuffers = true you are saying that you have written to e.OutputBuffers which you haven't. So they will just contain uninitialized data.
If you want to see an example of low-level manipulation of ASIO buffers, then check out my ASIO patch bay sample project.

Adding message to faceContext is not working in Java EE7 run on glassFish?

I am doing the tutorial on Java EE7 that comes with glassFish installation. It is also available here. The code is present in glassFish server installation directory
/glassFish_installation/glassfish4/docs/javaee-tutorial/examples/cdi/guessnumber-cdi.
The code works fine as it is. It currently displays correct! when a user correctly guesses the number but does not display failed at end of the game. so I introduced, just one minor change to display the failed message. I have added comments right above the relevant change in code.
Somehow, this change did not help. That is, the at the end of the game, failed message is not displayed.
But the game works as usual. I would like to know why this did not work and how to correct it?
Thanks
public class UserNumberBean implements Serializable {
private static final long serialVersionUID = -7698506329160109476L;
private int number;
private Integer userNumber;
private int minimum;
private int remainingGuesses;
#Inject
#MaxNumber
private int maxNumber;
private int maximum;
#Inject
#Random
Instance<Integer> randomInt;
public UserNumberBean() {
}
public int getNumber() {
return number;
}
public void setUserNumber(Integer user_number) {
userNumber = user_number;
}
public Integer getUserNumber() {
return userNumber;
}
public int getMaximum() {
return (this.maximum);
}
public void setMaximum(int maximum) {
this.maximum = maximum;
}
public int getMinimum() {
return (this.minimum);
}
public void setMinimum(int minimum) {
this.minimum = minimum;
}
public int getRemainingGuesses() {
return remainingGuesses;
}
public String check() throws InterruptedException {
if (userNumber > number) {
maximum = userNumber - 1;
}
if (userNumber < number) {
minimum = userNumber + 1;
}
if (userNumber == number) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("Correct!"));
}
//if remainingGuesses is less than or equal to zero, display failed message
//-----------------------------------------------
if (remainingGuesses-- <= 0) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("failed "));
}
return null;
}
#PostConstruct
public void reset() {
this.minimum = 0;
this.userNumber = 0;
this.remainingGuesses = 10;
this.maximum = maxNumber;
this.number = randomInt.get();
}
public void validateNumberRange(FacesContext context,
UIComponent toValidate,
Object value) {
int input = (Integer) value;
if (input < minimum || input > maximum) {
((UIInput) toValidate).setValid(false);
FacesMessage message = new FacesMessage("Invalid guess");
context.addMessage(toValidate.getClientId(context), message);
}
}
}
Adding the FacesMessage is actually working, the problem is that you are using postdecrement in your condition.
Postdecrement, as the name suggests, is decremented AFTER the execution of the statement containing the postdecrement.
That means, if you write:
if (remainingGuesses-- <= 0) {
the var remainingGuesses is decremented after the if-condition was evaluated.
In your case, when the last guess is checked, remainingGuesses is actually 1 and therefore the if-condition is not true and the message is not added.
Different obvious solutions:
if (remainingGuesses-- <= 1) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("failed "));
}
or
if (--remainingGuesses <= 0) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("failed "));
}
or
remainingGuesses--;
if (remainingGuesses <= 0) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("failed "));
}
See also:
Is there a difference between x++ and ++x in java?
Difference between i++ and ++i in a loop?

CPU Emulation and locking to a specific clock speed

If you had read my other question, you'll know I've spent this weekend putting together a 6502 CPU emulator as a programming exercise.
The CPU emulator is mostly complete, and seems to be fairly accurate from my limited testing, however it is running incredibly fast, and I want to throttle it down to the actual clock speed of the machine.
My current test loop is this:
// Just loop infinitely.
while (1 == 1)
{
CPU.ClockCyclesBeforeNext--;
if (CPU.ClockCyclesBeforeNext <= 0)
{
// Find out how many clock cycles this instruction will take
CPU.ClockCyclesBeforeNext = CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].CpuCycles;
// Run the instruction
CPU.ExecuteInstruction(CPU.Memory[CPU.PC]);
// Debugging Info
CPU.DumpDebug();
Console.WriteLine(CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].ArgumentLength);
// Move to next instruction
CPU.PC += 1 + CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].ArgumentLength;
}
}
As you can tell, each opcode takes a specific amount of time to complete, so I do not run the next instruction until I count down the CPU Cycle clock. This provides proper timing between opcodes, its just that the entire thing runs way to fast.
The targeted CPU speed is 1.79mhz, however I'd like whatever solution to the clock issue to keep the speed at 1.79mhz even as I add complexity, so I don't have to adjust it up.
Any ideas?
I wrote a Z80 emulator many years ago, and to do cycle accurate execution, I divided the clock rate into a number of small blocks and had the core execute that many clock cycles. In my case, I tied it to the frame rate of the game system I was emulating. Each opcode knew how many cycles it took to execute and the core would keep running opcodes until the specified number of cycles had been executed. I had an outer run loop that would run the cpu core, and run other parts of the emulated system and then sleep until the start time of the next iteration.
EDIT: Adding example of run loop.
int execute_run_loop( int cycles )
{
int n = 0;
while( n < cycles )
{
/* Returns number of cycles executed */
n += execute_next_opcode();
}
return n;
}
Hope this helps.
Take a look at the original quicktime documentation for inspiration.
It was written a long time ago, when displaying video meant just swapping still frames at high enough speed, but the Apple guys decided they needed a full time-management framework. The design at first looks overengineered, but it let them deal with widely different speed requirements and keep them tightly synchronized.
you're fortunate that 6502 has deterministic time behaviour, the exact time each instruction takes is well documented; but it's not constant. some instructions take 2 cycles, other 3. Just like frames in QuickTime, a video doesn't have a 'frames per second' parameter, each frame tells how long it wants to be in screen.
Since modern CPU's are so non-deterministic, and multitasking OS's can even freeze for a few miliseconds (virtual memory!), you should keep a tab if you're behind schedule, or if you can take a few microseconds nap.
As jfk says, the most common way to do this is tie the cpu speed to the vertical refresh of the (emulated) video output.
Pick a number of cycles to run per video frame. This will often be machine-specific but you can calculate it by something like :
cycles = clock speed in Hz / required frames-per-second
Then you also get to do a sleep until the video update is hit, at which point you start the next n cycles of CPU emulation.
If you're emulating something in particular then you just need to look up the fps rate and processor speed to get this approximately right.
EDIT: If you don't have any external timing requirements then it is normal for an emulator to just run as fast as it possibly can. Sometimes this is a desired effect and sometimes not :)
I would use the clock cycles to calculate time and them sleep the difference in time. Of course, to do this, you need a high-resolution clock. They way you are doing it is going to spike the CPU in spinning loops.
Yes, as said before most of the time you don't need a CPU emulator to emulate instructions at the same speed of the real thing. What user perceive is the output of the computation (i.e. audio and video outputs) so you only need to be in sync with such outputs which doesn't mean you must have necessarily an exact CPU emulation speed.
In other words, if the frame rate of the video input is, let's say, 50Hz, then let the CPU emulator run as fast as it can to draw the screen but be sure to output the screen frames at the correct rate (50Hz). From an external point of view your emulator is emulating at the correct speed.
Trying to be cycle exact even in the execution time is a non-sense on a multi-tasking OS like Windows or Linux because the emulator instruction time (tipically 1uS for vintage 80s CPUs) and the scheduling time slot of the modern OS are comparable.
Trying to output something at a 50Hz rate is a much simpler task you can do very good on any modern machine
Another option is available if audio emulation is implemented, and if audio output is tied to the system/CPU clock. In particular I know that this is the case with the 8-bit Apple ][ computers.
Usually sound is generated in buffers of a fixed size (which is a fixed time), so operation (generation of data etc) of these buffers can be tied to CPU throughput via synchronization primitives.
I am in the process of making something a little more general use case based, such as the ability to convert time to an estimated amount of instructions and vice versa.
The project homepage is # http://net7mma.codeplex.com
The code starts like this: (I think)
#region Copyright
/*
This file came from Managed Media Aggregation, You can always find the latest version # https://net7mma.codeplex.com/
Julius.Friedman#gmail.com / (SR. Software Engineer ASTI Transportation Inc. http://www.asti-trans.com)
Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction,
* including without limitation the rights to :
* use,
* copy,
* modify,
* merge,
* publish,
* distribute,
* sublicense,
* and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
*
* JuliusFriedman#gmail.com should be contacted for further details.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE,
* ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* v//
*/
#endregion
namespace Media.Concepts.Classes
{
//Windows.Media.Clock has a fairly complex but complete API
/// <summary>
/// Provides a clock with a given offset and calendar.
/// </summary>
public class Clock : Media.Common.BaseDisposable
{
static bool GC = false;
#region Fields
/// <summary>
/// Indicates when the clock was created
/// </summary>
public readonly System.DateTimeOffset Created;
/// <summary>
/// The calendar system of the clock
/// </summary>
public readonly System.Globalization.Calendar Calendar;
/// <summary>
/// The amount of ticks which occur per update of the <see cref="System.Environment.TickCount"/> member.
/// </summary>
public readonly long TicksPerUpdate;
/// <summary>
/// The amount of instructions which occured when synchronizing with the system clock.
/// </summary>
public readonly long InstructionsPerClockUpdate;
#endregion
#region Properties
/// <summary>
/// The TimeZone offset of the clock from UTC
/// </summary>
public System.TimeSpan Offset { get { return Created.Offset; } }
/// <summary>
/// The average amount of operations per tick.
/// </summary>
public long AverageOperationsPerTick { get { return InstructionsPerClockUpdate / TicksPerUpdate; } }
/// <summary>
/// The <see cref="System.TimeSpan"/> which represents <see cref="TicksPerUpdate"/> as an amount of time.
/// </summary>
public System.TimeSpan SystemClockResolution { get { return System.TimeSpan.FromTicks(TicksPerUpdate); } }
/// <summary>
/// Return the current system time in the TimeZone offset of this clock
/// </summary>
public System.DateTimeOffset Now { get { return System.DateTimeOffset.Now.ToOffset(Offset).Add(new System.TimeSpan((long)(AverageOperationsPerTick / System.TimeSpan.TicksPerMillisecond))); } }
/// <summary>
/// Return the current system time in the TimeZone offset of this clock converter to UniversalTime.
/// </summary>
public System.DateTimeOffset UtcNow { get { return Now.ToUniversalTime(); } }
//public bool IsUtc { get { return Offset == System.TimeSpan.Zero; } }
//public bool IsDaylightSavingTime { get { return Created.LocalDateTime.IsDaylightSavingTime(); } }
#endregion
#region Constructor
/// <summary>
/// Creates a clock using the system's current timezone and calendar.
/// The system clock is profiled to determine it's accuracy
/// <see cref="System.DateTimeOffset.Now.Offset"/>
/// <see cref="System.Globalization.CultureInfo.CurrentCulture.Calendar"/>
/// </summary>
public Clock(bool shouldDispose = true)
: this(System.DateTimeOffset.Now.Offset, System.Globalization.CultureInfo.CurrentCulture.Calendar, shouldDispose)
{
try { if (false == GC && System.Runtime.GCSettings.LatencyMode != System.Runtime.GCLatencyMode.NoGCRegion) GC = System.GC.TryStartNoGCRegion(0); }
catch { }
finally
{
System.Threading.Thread.BeginCriticalRegion();
//Sample the TickCount
long ticksStart = System.Environment.TickCount,
ticksEnd;
//Continually sample the TickCount. while the value has not changed increment InstructionsPerClockUpdate
while ((ticksEnd = System.Environment.TickCount) == ticksStart) ++InstructionsPerClockUpdate; //+= 4; Read,Assign,Compare,Increment
//How many ticks occur per update of TickCount
TicksPerUpdate = ticksEnd - ticksStart;
System.Threading.Thread.EndCriticalRegion();
}
}
/// <summary>
/// Constructs a new clock using the given TimeZone offset and Calendar system
/// </summary>
/// <param name="timeZoneOffset"></param>
/// <param name="calendar"></param>
/// <param name="shouldDispose">Indicates if the instace should be diposed when Dispose is called.</param>
public Clock(System.TimeSpan timeZoneOffset, System.Globalization.Calendar calendar, bool shouldDispose = true)
{
//Allow disposal
ShouldDispose = shouldDispose;
Calendar = System.Globalization.CultureInfo.CurrentCulture.Calendar;
Created = new System.DateTimeOffset(System.DateTime.Now, timeZoneOffset);
}
#endregion
#region Overrides
public override void Dispose()
{
if (false == ShouldDispose) return;
base.Dispose();
try
{
if (System.Runtime.GCSettings.LatencyMode == System.Runtime.GCLatencyMode.NoGCRegion)
{
System.GC.EndNoGCRegion();
GC = false;
}
}
catch { }
}
#endregion
//Methods or statics for OperationCountToTimeSpan? (Estimate)
public void NanoSleep(int nanos)
{
Clock.NanoSleep((long)nanos);
}
public static void NanoSleep(long nanos)
{
System.Threading.Thread.BeginCriticalRegion();
NanoSleep(ref nanos);
System.Threading.Thread.EndCriticalRegion();
}
static void NanoSleep(ref long nanos)
{
try
{
unchecked
{
while (Common.Binary.Clamp(--nanos, 0, 1) >= 2)
{
/* if(--nanos % 2 == 0) */
NanoSleep(long.MinValue); //nanos -= 1 + (ops / (ulong)AverageOperationsPerTick);// *10;
}
}
}
catch
{
return;
}
}
}
}
Once you have some type of layman clock implementation you advance to something like a Timer
/// <summary>
/// Provides a Timer implementation which can be used across all platforms and does not rely on the existing Timer implementation.
/// </summary>
public class Timer : Common.BaseDisposable
{
readonly System.Threading.Thread m_Counter; // m_Consumer, m_Producer
internal System.TimeSpan m_Frequency;
internal ulong m_Ops = 0, m_Ticks = 0;
bool m_Enabled;
internal System.DateTimeOffset m_Started;
public delegate void TickEvent(ref long ticks);
public event TickEvent Tick;
public bool Enabled { get { return m_Enabled; } set { m_Enabled = value; } }
public System.TimeSpan Frequency { get { return m_Frequency; } }
internal ulong m_Bias;
//
//Could just use a single int, 32 bits is more than enough.
//uint m_Flags;
//
readonly internal Clock m_Clock = new Clock();
readonly internal System.Collections.Generic.Queue<long> Producer;
void Count()
{
System.Threading.Thread Event = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
System.Threading.Thread.BeginCriticalRegion();
long sample;
AfterSample:
try
{
Top:
System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;
while (m_Enabled && Producer.Count >= 1)
{
sample = Producer.Dequeue();
Tick(ref sample);
}
System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest;
if (false == m_Enabled) return;
while (m_Enabled && Producer.Count == 0) if(m_Counter.IsAlive) m_Counter.Join(0); //++m_Ops;
goto Top;
}
catch { if (false == m_Enabled) return; goto AfterSample; }
finally { System.Threading.Thread.EndCriticalRegion(); }
}))
{
IsBackground = false,
Priority = System.Threading.ThreadPriority.AboveNormal
};
Event.TrySetApartmentState(System.Threading.ApartmentState.MTA);
Event.Start();
Approximate:
ulong approximate = (ulong)Common.Binary.Clamp((m_Clock.AverageOperationsPerTick / (Frequency.Ticks + 1)), 1, ulong.MaxValue);
try
{
m_Started = m_Clock.Now;
System.Threading.Thread.BeginCriticalRegion();
unchecked
{
Start:
if (IsDisposed) return;
switch (++m_Ops)
{
default:
{
if (m_Bias + ++m_Ops >= approximate)
{
System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;
Producer.Enqueue((long)m_Ticks++);
ulong x = ++m_Ops / approximate;
while (1 > --x /*&& Producer.Count <= m_Frequency.Ticks*/) Producer.Enqueue((long)++m_Ticks);
m_Ops = (++m_Ops * m_Ticks) - (m_Bias = ++m_Ops / approximate);
System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest;
}
if(Event != null) Event.Join(m_Frequency);
goto Start;
}
}
}
}
catch (System.Threading.ThreadAbortException) { if (m_Enabled) goto Approximate; System.Threading.Thread.ResetAbort(); }
catch (System.OutOfMemoryException) { if ((ulong)Producer.Count > approximate) Producer.Clear(); if (m_Enabled) goto Approximate; }
catch { if (m_Enabled) goto Approximate; }
finally
{
Event = null;
System.Threading.Thread.EndCriticalRegion();
}
}
public Timer(System.TimeSpan frequency)
{
Producer = new System.Collections.Generic.Queue<long>((int)(m_Frequency = frequency).Ticks * 10);
m_Counter = new System.Threading.Thread(new System.Threading.ThreadStart(Count))
{
IsBackground = false,
Priority = System.Threading.ThreadPriority.AboveNormal
};
m_Counter.TrySetApartmentState(System.Threading.ApartmentState.MTA);
Tick = delegate { m_Ops += 1 + m_Bias; };
}
public void Start()
{
if (m_Enabled) return;
m_Enabled = true;
m_Counter.Start();
var p = System.Threading.Thread.CurrentThread.Priority;
System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest;
while (m_Ops == 0) m_Counter.Join(0); //m_Clock.NanoSleep(0);
System.Threading.Thread.CurrentThread.Priority = p;
}
public void Stop()
{
m_Enabled = false;
}
void Change(System.TimeSpan interval, System.TimeSpan dueTime)
{
m_Enabled = false;
m_Frequency = interval;
m_Enabled = true;
}
delegate void ElapsedEvent(object sender, object args);
public override void Dispose()
{
if (IsDisposed) return;
base.Dispose();
Stop();
try { m_Counter.Abort(m_Frequency); }
catch (System.Threading.ThreadAbortException) { System.Threading.Thread.ResetAbort(); }
catch { }
Tick = null;
//Producer.Clear();
}
}
Then you can really replicate some logic using something like
/// <summary>
/// Provides a completely managed implementation of <see cref="System.Diagnostics.Stopwatch"/> which expresses time in the same units as <see cref="System.TimeSpan"/>.
/// </summary>
public class Stopwatch : Common.BaseDisposable
{
internal Timer Timer;
long Units;
public bool Enabled { get { return Timer != null && Timer.Enabled; } }
public double ElapsedMicroseconds { get { return Units * Media.Common.Extensions.TimeSpan.TimeSpanExtensions.TotalMicroseconds(Timer.Frequency); } }
public double ElapsedMilliseconds { get { return Units * Timer.Frequency.TotalMilliseconds; } }
public double ElapsedSeconds { get { return Units * Timer.Frequency.TotalSeconds; } }
//public System.TimeSpan Elapsed { get { return System.TimeSpan.FromMilliseconds(ElapsedMilliseconds / System.TimeSpan.TicksPerMillisecond); } }
public System.TimeSpan Elapsed
{
get
{
switch (Units)
{
case 0: return System.TimeSpan.Zero;
default:
{
System.TimeSpan taken = System.DateTime.UtcNow - Timer.m_Started;
return taken.Add(new System.TimeSpan(Units * Timer.Frequency.Ticks));
//System.TimeSpan additional = new System.TimeSpan(Media.Common.Extensions.Math.MathExtensions.Clamp(Units, 0, Timer.Frequency.Ticks));
//return taken.Add(additional);
}
}
//////The maximum amount of times the timer can elapse in the given frequency
////double maxCount = (taken.TotalMilliseconds / Timer.Frequency.TotalMilliseconds) / ElapsedMilliseconds;
////if (Units > maxCount)
////{
//// //How many more times the event was fired than needed
//// double overage = (maxCount - Units);
//// additional = new System.TimeSpan(System.Convert.ToInt64(Media.Common.Extensions.Math.MathExtensions.Clamp(Units, overage, maxCount)));
//// //return taken.Add(new System.TimeSpan((long)Media.Common.Extensions.Math.MathExtensions.Clamp(Units, overage, maxCount)));
////}
//////return taken.Add(new System.TimeSpan(Units));
}
}
public void Start()
{
if (Enabled) return;
Units = 0;
//Create a Timer that will elapse every OneTick //`OneMicrosecond`
Timer = new Timer(Media.Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick);
//Handle the event by incrementing count
Timer.Tick += Count;
Timer.Start();
}
public void Stop()
{
if (false == Enabled) return;
Timer.Stop();
Timer.Dispose();
}
void Count(ref long count) { ++Units; }
}
Finally, create something semi useful e.g. a Bus and then perhaps a virtual screen to emit data to the bus...
public abstract class Bus : Common.CommonDisposable
{
public readonly Timer Clock = new Timer(Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick);
public Bus() : base(false) { Clock.Start(); }
}
public class ClockedBus : Bus
{
long FrequencyHz, Maximum, End;
readonly Queue<byte[]> Input = new Queue<byte[]>(), Output = new Queue<byte[]>();
readonly double m_Bias;
public ClockedBus(long frequencyHz, double bias = 1.5)
{
m_Bias = bias;
cache = Clock.m_Clock.InstructionsPerClockUpdate / 1000;
SetFrequency(frequencyHz);
Clock.Tick += Clock_Tick;
Clock.Start();
}
public void SetFrequency(long frequencyHz)
{
FrequencyHz = frequencyHz;
//Clock.m_Frequency = new TimeSpan(Clock.m_Clock.InstructionsPerClockUpdate / 1000);
//Maximum = System.TimeSpan.TicksPerSecond / Clock.m_Clock.InstructionsPerClockUpdate;
//Maximum = Clock.m_Clock.InstructionsPerClockUpdate / System.TimeSpan.TicksPerSecond;
Maximum = cache / (cache / FrequencyHz);
Maximum *= System.TimeSpan.TicksPerSecond;
Maximum = (cache / FrequencyHz);
End = Maximum * 2;
Clock.m_Frequency = new TimeSpan(Maximum);
if (cache < frequencyHz * m_Bias) throw new Exception("Cannot obtain stable clock");
Clock.Producer.Clear();
}
public override void Dispose()
{
ShouldDispose = true;
Clock.Tick -= Clock_Tick;
Clock.Stop();
Clock.Dispose();
base.Dispose();
}
~ClockedBus() { Dispose(); }
long sample = 0, steps = 0, count = 0, avg = 0, cache = 1;
void Clock_Tick(ref long ticks)
{
if (ShouldDispose == false && false == IsDisposed)
{
//Console.WriteLine("#ops=>" + Clock.m_Ops + " #ticks=>" + Clock.m_Ticks + " #Lticks=>" + ticks + "#=>" + Clock.m_Clock.Now.TimeOfDay + "#=>" + (Clock.m_Clock.Now - Clock.m_Clock.Created));
steps = sample;
sample = ticks;
++count;
System.ConsoleColor f = System.Console.ForegroundColor;
if (count <= Maximum)
{
System.Console.BackgroundColor = ConsoleColor.Yellow;
System.Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("count=> " + count + "#=>" + Clock.m_Clock.Now.TimeOfDay + "#=>" + (Clock.m_Clock.Now - Clock.m_Clock.Created) + " - " + DateTime.UtcNow.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt"));
avg = Maximum / count;
if (Clock.m_Clock.InstructionsPerClockUpdate / count > Maximum)
{
System.Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("---- Over InstructionsPerClockUpdate ----" + FrequencyHz);
}
}
else if (count >= End)
{
System.Console.BackgroundColor = ConsoleColor.Black;
System.Console.ForegroundColor = ConsoleColor.Blue;
avg = Maximum / count;
Console.WriteLine("avg=> " + avg + "#=>" + FrequencyHz);
count = 0;
}
}
}
//Read, Write at Frequency
}
public class VirtualScreen
{
TimeSpan RefreshRate;
bool VerticalSync;
int Width, Height;
Common.MemorySegment DisplayMemory, BackBuffer, DisplayBuffer;
}
Here is how I tested the StopWatch
internal class StopWatchTests
{
public void TestForOneMicrosecond()
{
System.Collections.Generic.List<System.Tuple<bool, System.TimeSpan, System.TimeSpan>> l = new System.Collections.Generic.List<System.Tuple<bool, System.TimeSpan, System.TimeSpan>>();
//Create a Timer that will elapse every `OneMicrosecond`
for (int i = 0; i <= 250; ++i) using (Media.Concepts.Classes.Stopwatch sw = new Media.Concepts.Classes.Stopwatch())
{
var started = System.DateTime.UtcNow;
System.Console.WriteLine("Started: " + started.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt"));
//Define some amount of time
System.TimeSpan sleepTime = Media.Common.Extensions.TimeSpan.TimeSpanExtensions.OneMicrosecond;
System.Diagnostics.Stopwatch testSw = new System.Diagnostics.Stopwatch();
//Start
testSw.Start();
//Start
sw.Start();
while (testSw.Elapsed.Ticks < sleepTime.Ticks - (Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick + Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick).Ticks)
sw.Timer.m_Clock.NanoSleep(0); //System.Threading.Thread.SpinWait(0);
//Sleep the desired amount
//System.Threading.Thread.Sleep(sleepTime);
//Stop
testSw.Stop();
//Stop
sw.Stop();
var finished = System.DateTime.UtcNow;
var taken = finished - started;
var cc = System.Console.ForegroundColor;
System.Console.WriteLine("Finished: " + finished.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt"));
System.Console.WriteLine("Sleep Time: " + sleepTime.ToString());
System.Console.WriteLine("Real Taken Total: " + taken.ToString());
if (taken > sleepTime)
{
System.Console.ForegroundColor = System.ConsoleColor.Red;
System.Console.WriteLine("Missed by: " + (taken - sleepTime));
}
else
{
System.Console.ForegroundColor = System.ConsoleColor.Green;
System.Console.WriteLine("Still have: " + (sleepTime - taken));
}
System.Console.ForegroundColor = cc;
System.Console.WriteLine("Real Taken msec Total: " + taken.TotalMilliseconds.ToString());
System.Console.WriteLine("Real Taken sec Total: " + taken.TotalSeconds.ToString());
System.Console.WriteLine("Real Taken μs Total: " + Media.Common.Extensions.TimeSpan.TimeSpanExtensions.TotalMicroseconds(taken).ToString());
System.Console.WriteLine("Managed Taken Total: " + sw.Elapsed.ToString());
System.Console.WriteLine("Diagnostic Taken Total: " + testSw.Elapsed.ToString());
System.Console.WriteLine("Diagnostic Elapsed Seconds Total: " + ((testSw.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency)));
//Write the rough amount of time taken in micro seconds
System.Console.WriteLine("Managed Time Estimated Taken: " + sw.ElapsedMicroseconds + "μs");
//Write the rough amount of time taken in micro seconds
System.Console.WriteLine("Diagnostic Time Estimated Taken: " + Media.Common.Extensions.TimeSpan.TimeSpanExtensions.TotalMicroseconds(testSw.Elapsed) + "μs");
System.Console.WriteLine("Managed Time Estimated Taken: " + sw.ElapsedMilliseconds);
System.Console.WriteLine("Diagnostic Time Estimated Taken: " + testSw.ElapsedMilliseconds);
System.Console.WriteLine("Managed Time Estimated Taken: " + sw.ElapsedSeconds);
System.Console.WriteLine("Diagnostic Time Estimated Taken: " + testSw.Elapsed.TotalSeconds);
if (sw.Elapsed < testSw.Elapsed)
{
System.Console.WriteLine("Faster than Diagnostic StopWatch");
l.Add(new System.Tuple<bool, System.TimeSpan, System.TimeSpan>(true, sw.Elapsed, testSw.Elapsed));
}
else if (sw.Elapsed > testSw.Elapsed)
{
System.Console.WriteLine("Slower than Diagnostic StopWatch");
l.Add(new System.Tuple<bool, System.TimeSpan, System.TimeSpan>(false, sw.Elapsed, testSw.Elapsed));
}
else
{
System.Console.WriteLine("Equal to Diagnostic StopWatch");
l.Add(new System.Tuple<bool, System.TimeSpan, System.TimeSpan>(true, sw.Elapsed, testSw.Elapsed));
}
}
int w = 0, f = 0;
var cc2 = System.Console.ForegroundColor;
foreach (var t in l)
{
if (t.Item1)
{
System.Console.ForegroundColor = System.ConsoleColor.Green;
++w; System.Console.WriteLine("Faster than Diagnostic StopWatch by: " + (t.Item3 - t.Item2));
}
else
{
System.Console.ForegroundColor = System.ConsoleColor.Red;
++f; System.Console.WriteLine("Slower than Diagnostic StopWatch by: " + (t.Item2 - t.Item3));
}
}
System.Console.ForegroundColor = System.ConsoleColor.Green;
System.Console.WriteLine("Wins = " + w);
System.Console.ForegroundColor = System.ConsoleColor.Red;
System.Console.WriteLine("Loss = " + f);
System.Console.ForegroundColor = cc2;
}
}