Hello Every Body,
I am trying to build a Arduino Uno R3 based Doppler
that connects through USB to the PC and MyMapping.exe program.
So see the shield and my initial improved sketch
I updated the sketch and added a shield picture. ToDay 24-05-2015
Again updated the Sketch 01-05-2015
Fixed several issues
Still working on the code
/*
((C))PA3BNX
Trying to create a SuperSimpleArduinoDoppler on Arduino Uno R3
For this there is a special Arduino Com/USB input in MyMapping to show the headings on maps
@12-04-2015
@13-04-2015
@15-04-2015
@17-04-2015
@22-04-2015
@24-04-2015
@25-04-2015
@26-04-2015
@27-04-2015
@29-04-2015
@01-05-2015
Not ready yet!
Try max232 for Am WhatsonWatt DF to
Need extra pin for that or use antPin ?
Arrays are zero based !!!!
Explaining Arduino pins.
antPins[] Connect to antenna switcher pins
refpa3bnx Connect to audio lowpass filter for soundcard referency input
kPins[] Connect to kwadrant C's 4u7
kPins[12] Connect to discharge transistor //Not yet done in hardware
kPinAnalogue Connect to sum point of 4 cap's
*/
//Byte= 0>255
//Int= -32768>32767
//String Serial params
String inputString = ""; //Received serial string
//Boolean
boolean bStringComplete = false; //Complete '\n' rxed serial string
boolean bNoSetKpins = false; //Do not change kPin[] because I am reading/empty them now
boolean bMultiPathAverage = false; //Multipath heading found in received headings from Averaged
//Const Integer
//Must be modified to right pin number arduino and arduino shield harddware
//Antenna Control Pins & refpa3bnx //Never use pins 0 and 1 because the UART must also function
//So I started with pin 2 to
byte antPin[4] = {2,3,4,5}; //Set antenna control pins (Not const because it can CCW/CW)
byte max232Pin[2] = {7,8}; //Set Max232Pin vor AM plate antenna
const byte refpa3bnx = 6; //Pin for Referency frequency output to soundcard
//Kwadrant Capicity switches & analogue SCF read pin
const byte kPin[5] = {9,10,11,12,13}; //4 pins caps 4u7 Last Pin in array = Discharge caps by bc547
const byte kPinAnalog = A0; //A0 read voltage across one 4u7 cap pin A0 to A5
const int cOffset = 511; // .5 * 1023 caps offset
//Squelch Signals
const byte cSqlclosed = 0;
const byte cSqlclosed2open = 1;
const byte cSqlopen2closed = 2;
const byte cSqloverflow = 3; //Capicitors are full
//
//NoDegreesMaxMinValues
const int cNodegrees = 999; //Squelch No Signal or cSqlopen2closed
const int cMaxTime = 30; //15 Sec/cMainLoopDelay Max time
const int cMainLoopDelay = 500; //How fast the main loop runs in mSec
const int cMinAudioAmplitude = 100; //There is enough signal to SinCosDetect
//Float const
//const float pi = 4 * atan(1); //PI 3.14159
//const float rad = pi/180;
const float rad1 = 180/PI; //57.295827
//const float TwoPi = 2 * pi; //TWO_PI
//Integer
//Array with the 4 kwandrant analogue cap tensions These can be -511 to +511
int Cap[4] = {0,0,0,0};
int Calibrate= 0; //degrees default
byte sqlstatus = cSqlclosed;
//------------ISR Main Interrupt //This runs always--------------------
ISR(TIMER0_COMPA_vect)
{
static byte c;
//Do the Ant and Cap rotation here
if (c==4){
c=0;
}
SetAntPins(c);
SetMax232Pins(c);
SetKpinsHigh;
c++;
}
//---------Functions now-------------------
//Max232Pins for AM Whatson Watt RDF
void SetMax232Pins(byte t){
switch (t){
case 0:
digitalWrite(max232Pin[0],HIGH);
digitalWrite(max232Pin[1],LOW);
break;
case 1:
digitalWrite(max232Pin[0],HIGH);
digitalWrite(max232Pin[1],HIGH);
break;
case 2:
digitalWrite(max232Pin[0],LOW);
digitalWrite(max232Pin[1],LOW);
break;
case 3:
digitalWrite(max232Pin[0],LOW);
digitalWrite(max232Pin[1],HIGH);
break;
}
}
//Antenna Control Lines Just counts 1234 1234 etc
void SetAntPins(byte t){
switch (t){
case 0:
digitalWrite(antPin[0], HIGH);
digitalWrite(antPin[3], LOW);
digitalWrite(refpa3bnx,HIGH);
break;
case 1:
digitalWrite(antPin[1], HIGH);
digitalWrite(antPin[0], LOW);
break;
case 2:
digitalWrite(antPin[2], HIGH);
digitalWrite(antPin[1], LOW);
digitalWrite(refpa3bnx,LOW);
break;
case 3:
digitalWrite(antPin[3], HIGH);
digitalWrite(antPin[2], LOW);
break;
}
}
//Set kwadrant Cap Pins just High
void SetKpinsHigh(){
if (bNoSetKpins==false){
//Set all HIGH
for (int i=0 ;i<4;i++){
digitalWrite(kPin[i],HIGH);
}
}
}
//Get the 4 analogue value's in Cap[]
void GetCapValues(){
//All kwadrant Cap Pins HIGH
for (byte i=0; i<4; i++)
{
digitalWrite(kPin[i],HIGH);
}
//Read them one by one
digitalWrite(kPin[0],LOW);
Cap[0] = analogRead(kPinAnalog)-cOffset;
digitalWrite(kPin[0],HIGH);
digitalWrite(kPin[1],LOW);
Cap[1] = analogRead(kPinAnalog)-cOffset;
digitalWrite(kPin[1],HIGH);
digitalWrite(kPin[2],LOW);
Cap[2] = analogRead(kPinAnalog)-cOffset;
digitalWrite(kPin[2],HIGH);
digitalWrite(kPin[3],LOW);
Cap[3] = analogRead(kPinAnalog)-cOffset;
digitalWrite(kPin[3],HIGH);
}
//Empty the 4 capicitors by discharging to 1/2 U
void EmptyCaps(){
for (byte i=0; i<4; i++)
{
pinMode(kPin[i],LOW);
}
digitalWrite(kPin[5],HIGH); //Discharge cap pin bc547
delay(2100); //Let the 4 caps charge to 1/2 U to 99% 5 * (22k * (4,7uF * 4)) 2068 Sec
digitalWrite(kPin[5],LOW);
}
//-------------Squelch-----------------------
int Squelch(){
//Return the sqlstatus
//Try Find a full cap
int x;
//Find cap with highest voltage/tension
//Biggest value = cOffset (511)
for(byte i=0; i<4; i++)
{
if abs((Cap[i]) >= cOffset )
{
x = abs(Cap[i]);
}
}
//See if there is a Full Cap
//cOffset = Biggest value
if (x >= cOffset)
{
return cSqloverflow;
//Caps are full so need discharged
}
else if (x >= cMinAudioAmplitude)
{
return cSqlclosed2open;
}
else if (x < cMinAudioAmplitude)
{
return cSqlclosed;
}
}
//Detect Multipath in SCF Caps
boolean MultiPathDetectSCF(){
//Calc average of 4 kwadrants
//If to large asymetric value return true
//Can it do also the Quality 1-9 ?
int x;
for (int i=0 ; i<4 ; i++){
x += Cap[i];
}
//150 of max 510 is the MultiPath level ?
if (abs(x) > 150){
return true;
}
else
{
return false;
}
}
//Calibrate
int Cali(int degrees){
return LimitDegrees360(degrees(Calibrate + degrees));
}
//Return Agrelo formatted string % and 3 digits always
String Format3Degrees(int degrees){
String str;
int digits[3];
int reminder;
digits[0]=degrees/100;
reminder = degrees % 100;
digits[1]= reminder/10;
reminder = reminder % 10;
digits[2]=reminder;
str += '%';
str += digits[0];
str += digits[1];
str += digits[2];
return str;
}
//Limit Degrees 0 to 360 degrees
int LimitDegrees360(int d){
//Limit degrees 0 to 360 here
if (d >= 360){
return d - 360;
}
else if (d < 360)
{
return d;
}
else if (d < 0)
{
return 360 + d;
}
else
{
return cNodegrees;
}
}
//Average
int Average(int d){
//If d = 999 (cNoDegrees) then reset average
//Else return averaged degrees
//Integer
static int sum[4]; //Sum of counts for each kwadrant
static int c[4]; //Cap tension/voltage in 4 kwadrant
int xx[4] ={0,0,0,0}; //Adjacent kwadrants
int z = 0; //Most adjacent headings
int z1 = 0; //Other two kwandants headings
int y = 0; //Index where the most headings are
int avDegrees; //Averaged degrees
//Limit amount off averaged headings
if (c[0]+c[1]+c[2]+c[3] > cMaxTime ){
for (byte i=0; i<4 ; i++){
sum[i]=0;
c[i]=0;
return cNodegrees;
}
}
if (d!=cNodegrees){
if (d >= 0 && d < 90){
sum[0] += d;
c[0]++;
}
if (d >= 90 && d < 180){
sum[1] += d;
c[1]++;
}
if (d >= 180 && d < 270){
sum[2] += d;
c[2]++;
}
if (d >= 270 && d <= 360){
sum[3] += d;
c[3]++;
}
}
else
{
//Reset
for (byte i=0; i<4; i++){
sum[i]=0;
c[i]=0;
bMultiPathAverage = false;
}
return cNodegrees;
}
//Find 2 adjacent kwadrants with hold most counts
//Count all adjacents kwadrants
xx[1] = c[0]+c[1];
xx[2] = c[1]+c[2];
xx[3] = c[2]+c[3];
xx[4] = c[3]+c[0];
//Find out in witch two kwadrants the most headings are
for (byte i=0; i<4; i++){
if (xx[i] >= z){
z = xx[i]; //Most Headings
y = i; //Witch 2 adjacent Array Index
}
}
//Check for multipath
switch(z){
case 0:
z1 = 3;
break;
case 1:
z1 = 4;
break;
case 2:
z1 = 1;
break;
case 3:
z1 = 2;
break;
}
if (z > z1){
bMultiPathAverage = true;
}
else
{
bMultiPathAverage = false;
}
//Average the 2 most counts kwadrants
switch(y){
case 1:
avDegrees = (sum[0] + sum[1]) / c[y];
break;
case 2:
avDegrees = (sum[1] + sum[2]) / c[y];
break;
case 3:
avDegrees = (sum[2] + sum[3]) / c[y];
break;
case 4:
if (c[3] == 0 && c[0] > 0){
avDegrees = sum[0] / c[0];
break;
}
if (c[3] > 0 && c[0] == 0){
avDegrees = sum[3] / c[3];
break;
}
if (c[3] > 0 && c[0] > 0 ){
avDegrees = LimitDegrees360(((sum[3] / c[3]) + ((sum[0] / c[0]) + 360) / 2));
break;
}
}
if (d == cNodegrees){
return avDegrees;
}
}
//SinCosDetector Find the degrees from the 4 Cap[] tensions
int SinCosDetector(){
int SinSum;
int CosSum;
int x;
//In Cap[] is the tension value of 1 of the 4 C'S
SinSum = Cap[0] - Cap[2];
CosSum = Cap[1] - Cap[3];
if (SinSum == 0 && CosSum == 0){
return cNodegrees;
}
else
{
return LimitDegrees360(atan2(SinSum,CosSum) * rad1);
}
}
//=============SerialRx Event===========================
void serialEvent() {
while (Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
inputString += inChar;
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n') {
bStringComplete = true;
}
else
{
bStringComplete = false;
}
}
}
//===============
//------------------------------------Main Functions-----------------------------------------
//SetUp
void setup()
{
// Loop over the pin array and set them all to output:
Serial.begin(9600); // Opens serial port, sets data rate to 9600 bps
Serial.println("((C))PA3BNX Arduino Doppler");
Serial.println("%999"); //Squelch always closed at start
pinMode(refpa3bnx,OUTPUT);
digitalWrite(refpa3bnx,LOW);
pinMode( kPinAnalog,INPUT); //Okay here ????
pinMode(kPin[5],OUTPUT); //Discharge Pin bc547
digitalWrite(kPin[5],LOW);
pinMode(max232Pin[0],OUTPUT);
pinMode(max232Pin[1],OUTPUT);
digitalWrite(max232Pin[0],LOW);
digitalWrite(max232Pin[1],LOW);
for (byte i=0; i<4; i++){
pinMode(kPin[i],OUTPUT);
pinMode(antPin[i] ,OUTPUT);
digitalWrite(antPin[i],LOW); //antPin LOW
digitalWrite(kPin[i],LOW); //Set Cap pins to low so the can charge to 1/2U
}
Average(cNodegrees); //Reset all to 0
EmptyCaps; //All kPins to 1/2 U
//Now start ISR interrupt
// Timer0 is already used for millis() - we'll just interrupt somewhere
// in the middle and call the "Compare A" function below
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
// ToDo is there a default timer in microseconds to ?
// Interrupt is called once a millisecond
// I assume now doppler rotate frequency 4 mSec or 1/.004 = 250 Hz //Measured 244 Hz at refpa3bnx pin
}
//Main Loop
void loop() {
// ToDo
// GetCapValues
// Set the ant1,2,3,4 and refpa3bnx pins ISR
// Squelch status
// OverFlow Reset the 4 kwadrant capicitors
// Calibrate routine (add an degrees offset)
// SinCosDetector
// Send (averaged) and calibrated degrees out through the serialport
// Receive remote serial cmd's like CW/CCW and Degrees
//Integer
byte swap = antPin[1]; //90 degrees
int t;
bNoSetKpins = true;
GetCapValues();
bNoSetKpins = false;
SetKpinsHigh();
sqlstatus = Squelch(); //Squelch status check
if (sqlstatus == cSqloverflow || (t > cMaxTime))
{
t = 0;
bNoSetKpins = true;
EmptyCaps();
bNoSetKpins= false;
SetKpinsHigh();
}
else
{
//Relaxed sending degrees to serialport
if (sqlstatus == cSqlopen2closed && bMultiPathAverage == false)
{
if (MultiPathDetectSCF == false){
Serial.println(Format3Degrees(Cali(Average(SinCosDetector()))));
sqlstatus = cSqlclosed;
}
}
}
//Try to read Calibration, CW/CCW, Timing Parameters
//000<cr> <lf> from MyMapping.exe as calibrate cmd
serialEvent; //Call SerialEvent once in a while
if (bStringComplete == true){
//Swap 90 and 270 degrees pins
if (inputString == "CCW"){
if (swap != antPin[1])
{
swap=antPin[1];
antPin[1]=antPin[3];
antPin[3]=swap;
}
}
else if (inputString == "CW")
{
if (swap != antPin[1])
{
swap=antPin[1];
antPin[1]=antPin[3];
antPin[3]=swap;
}
}
else
{
Calibrate = inputString.toInt();
}
//Always Clear inputString Now
inputString="";
}
//Do always every loop
delay(cMainLoopDelay); //Wait 500 mSec for next try ? fast enough I think
t += 1; //Total loops to compared against cMaxTime
}
//End of sketch