Go Down

Topic: converting gps speed to pulses (Read 605 times) previous topic - next topic

engrjhnsn

What I am trying to do is use the TinyGPS++ or NeoGPS library to communicate to my Garmin 17x gps module. After getting the speed I want to convert it to a frequency output to input to my ecu for datalogging. I am keeping it easy so that 1 mph = 1 hz for now and would like to have 10x per sec update rate even though current sensor only updates at 1 sec.

I need some help with how to make this work. I have tried two different ways. One using micros to monitor time and change output bit and the other way is using timer 1 and sending different frequency to it.

The problem is that the output frequency is not stable and jumps around a lot. I am just not good enough with the arduino to get past this point.

any help is appreciated.

here is the code I have tried

Code: [Select]

#include <TinyGPS++.h>
#include <SoftwareSerial.h>

static const int RXPin = 2, TXPin = 3;
static const uint32_t GPSBaud = 4800;

static const int SpeedoPin = 8;

float vssPeriod; // was unsigned long for micros
unsigned long vssWidth;

unsigned long currentMicros;
unsigned long previousMicros;

float mph = 0.0;

float ppm = 3600;

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

void setup()
{
  //Serial.begin(9600);
  ss.begin(GPSBaud);
  pinMode(SpeedoPin, OUTPUT);
}

void loop()
{
  // This sketch displays information every time a new sentence is correctly encoded.

  while (ss.available() > 0)
  {
    if (gps.encode(ss.read()))
    {
      if (gps.speed.isValid())
      {
        mph = gps.speed.mph();
        //Serial.print(F("mph: ")), Serial.print(mph);
        //Serial.println(F(" "));
        //Serial.println(gps.speed.mph());
      }
    }
  }
 
  currentMicros = micros();
 
  mph = 120.00;
  vssPeriod = ppm / (mph * ppm);
  vssWidth = (vssPeriod * 0.5) * 1000000; // pulse width of period, assuming 50% duty cycle, not PWM
//vssWidth = 100000; // 5hz
//vssWidth = 10000; // 50hz
//vssWidth = 1000; // 500hz
//vssWidth = 100; // 5000hz
//vssWidth = 10; // 8400hz

  if ((currentMicros - previousMicros) >= vssWidth)
  {
    previousMicros = previousMicros + vssWidth;
    PINB = bit(0);
    //PINB = 0x00000001;
  }
   
  delay(500);
}


Code: [Select]

#include <avr/io.h>
#include <avr/interrupt.h>

#include <TinyGPS++.h>
#include <SoftwareSerial.h>

static const int RXPin = 2, TXPin = 3;
static const uint32_t GPSBaud = 4800;

static const int SpeedoPin = 9;

float vssPeriod; // was unsigned long for micros
unsigned long vssWidth;

float mph = 0.0;

float ppm = 3600;

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

void setup()
{
  //Serial.begin(9600);
  ss.begin(GPSBaud);
  pinMode(SpeedoPin, OUTPUT);
}

void loop()
{
  // This sketch displays information every time a new sentence is correctly encoded.

  while (ss.available() > 0)
  {
    if (gps.encode(ss.read()))
    {
      if (gps.speed.isValid())
      {
        mph = gps.speed.mph();
        //Serial.print(F("mph: ")), Serial.print(mph);
        //Serial.println(F(" "));
        //Serial.println(gps.speed.mph());
      }
    }
  }
 
  mph = 120.00;
  vssPeriod = ppm / (mph * ppm);
  //vssWidth = (vssPeriod * 0.5) * 1000000; // pulse width of period, assuming 50% duty cycle, not PWM
//vssWidth = 100000; // 5hz
//vssWidth = 10000; // 50hz
//vssWidth = 1000; // 500hz
//vssWidth = 100; // 5000hz
//vssWidth = 10; // 8400hz
  vssPeriod = 1 / vssPeriod;
  DFG(vssPeriod);
   
  delay(500);
 
}


void DFG(unsigned long tempfreq){
  cli();//disable interupts
  TCCR1A = 0;//registers for timer 1
  TCCR1B = 0;
  TCNT1=0;
  TCCR1A |= _BV(COM1A0) + _BV(COM1B0);
  TCCR1B |=_BV(WGM12);
  TCCR1C = _BV(FOC1A);
   if(tempfreq > 122 && tempfreq < 1000001){
  OCR1A = (8000000/tempfreq)-1;//#TIMER COUNTS
  TCCR1B |= _BV(CS10);
  }
  else if(tempfreq <= 122 && tempfreq > 15){
    OCR1A = (1000000/tempfreq)-1;
    TCCR1B |= _BV(CS11);
  }
  else if(tempfreq <= 15 && tempfreq > 4){
    OCR1A = (125000/tempfreq)-1;
    TCCR1B |= _BV(CS10) + _BV(CS11);
  }
 
  //TIMSK1 = _BV(OCIE1A);//TIMER1 COMPARE INTERUPT
  sei();//enable interupts
}

PaulS

Quote
The problem is that the output frequency is not stable and jumps around a lot.
Of course. Think about why that might be the case. What is causing the output? A timer interrupt. What else is using interrupts in your program?

SoftwareSerial is!

You will never get stable output until you get rid of SoftwareSerial. That probably means using a Mega with multiple hardware serial ports. Hardware serial uses interrupts, too, but they are less intrusive than the SoftwareSerial ones.
The art of getting good answers lies in asking good questions.

engrjhnsn

No I didn't know software serial is using interrupts so thank you for that. I don't really want to upgrade to mega, if anything I wanna go smaller package size.

Now I did get it to work so far with my timer example by using a flag and setting it while serial is getting data and once it's done and I have a new value I then update frequency to timer.

In my other example I'm not using the extra timer/interrupt but just check for elapsed time and bit bang to get frequency. I haven't got this one to work yet cause the serial gets in the way. This way might not work at all and I'll stick with timers.

aarg

If your app is not using the hardware serial port, you could connect the GPS input to that.
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

-dev

#4
Jul 23, 2015, 08:31 pm Last Edit: Jul 23, 2015, 08:38 pm by /dev
Quote
If your app is not using the hardware serial port, you could connect the GPS input to that.
+1!  As in start the GPS library on Serial, and do debug prints to SoftwareSerial.  It looks like you won't have the debug prints in your final system anyway.

For generating the pulses, I think you could use Timer1 in CTC mode to vary the frequency of a pulse on pin 9 or pin3 only.  Here's a thread that shows how to generate 38KHz, so you'll have to modify it to output (speed * 10) Hz.  According to the spec,



fCLK I/O is the CPU speed, 16MHz usually.  So to get 35Hz, you set the prescaler to 8, and OCR1A to 28571.  With the prescaler set to 8, the lowest frequency is 15.26Hz.  For 12Hz, you've got to set the prescaler to 64 and OCR1A to 10416.  Etc.  ;)

There are some other bits to set, like Waveform Generation mode and enabling pin 3 or 9 to run from OC1B or OC1A (Compare Output mode)... maybe someone else knows about a complete example for this?

Once you've got it set up, you should only have to change OCR1A and the prescaler in TCCR1B once per second, when you get a new speed from GPS.  The nice thing is those pulses always go out, independently of what your code is doing.  It's not affected by interrupts.

Cheers,
/dev


Of course, Nick Gammon has some good info here.

engrjhnsn

good points about using the hardware serial instead. I don't need it other than loading code on to the Arduino.

As for the Timer1 in CTC mode, I know I am using Timer1 in some way on pin 9, I got the code off of another thread here, but I will read up on it some more.

Yes Nick Gammon has really good info on his site. I bookmarked that link a long time ago and have read it a few times but putting it all together to make it do what I want is a different story.

It's kinda working now by just calling the timing function each time I get an update to mph. Just a small amount of jitter.

/dev, aren't you the developer of the NeoGPS library?

-dev

Well, plbbbt!  Your second code example is the CTC mode.  I didn't read all the way to the end after seeing the micros() in the first sketch.  It's not the easiest thing to read,  but that's what it is.  The first sketch is very susceptible to interrupts.

Maybe I don't know what you mean by jitter... from the first sketch, I assumed it was jitter between pulses.  The second sketch uses CTC, so the pulse train is as accurate as the CPU clock.  Are you talking about changing the frequency once per second, and the speed jumps around?  You can perform data smoothing on the speed values in many different ways (e.g., moving average).

The GPS speed value "wanders" just like position and altitude.  Without any other data source to correct the inherent GPS errors, that's what you get.  Sometimes you can tell the GPS device what kind of "platform" it's on: stationary, low speed (walking), high speed (driving) or airborne.  Then it can do some extra processing.  Your smoothing could also use assumptions of what's physically possible to limit the "jitter" of the speed.

Arduino drone projects use additional data sources, like an IMU, compass and barometer, to get a "better" estimate of speed, heading, position and altitude.

Quote
/dev, aren't you the developer of the NeoGPS library?
Guilty.

Cheers,
/dev

Go Up