in reply to Re: serial port tied filehandle autoflush
in thread serial port tied filehandle autoflush

Thanks for your response,

the serial port is a heading compass. It will give you heading strings like:

$HEHDT,123.49,T*12 $HEHDT,123.43,T*18 $HEHDT,123.43,T*18 $HEHDT,123.49,T*12 $HEHDT,123.49,T*12 $HEHDT,123.42,T*19

According to the direction from true north (0°-359.9°). The last value is a checksum.

I never write to the port. I just want the most recent heading (yes that's what I meant with last, sorry about that). The problem is the serial interface is rather fast. The output comes 10 times a second or one time a second.

In my main routine I don't want the heading that fast. I just want it when I need it. When I call my method I don't get the most recent value I get a value from the buffer.

There is many different sorts of compasses also some that output much more then just the heading. (GPS and other NMEA strings). I liked the tied filehandle approach (SerialPort::Port Methods for tied filehandles). My code already does what I want but I always retie or recreate the object. That way the first string I get is incomplete and thus invalid all the time plus the time I have to recreate the object, which is still fast but I don't believe it's the proper way.

Here's my two different Log::Log4perl output:

What I get:

05.12.2014-08:46:19 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving ¤EHDT,217.62,T*1F on /dev/nmea 05.12.2014-08:46:19 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:276)> + Not receiving valid NMEA data or cut off string: ¤EHDT,217.62,T*1F ! 05.12.2014-08:46:19 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving $HEHDT,217.62,T*1F on /dev/nmea 05.12.2014-08:46:19 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:292)> + $VAR1 = bless( { '_CKS_' => '1F', 'TrueHeading' => '217.62', '_STRING_' => '$HEHDT,217.62,T*1F', '_calcCKS_' => '1F', '_ELEMENT_' => 'HDT', 'HDTT' => 'T', '_ERROR_' => undef, '_DEVICE_' => 'HE' }, 'NMEAdata' ); 05.12.2014-08:46:19 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:295)> + Received true heading: 217.62 ----------------------------------------------------- 05.12.2014-08:46:20 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving ¦L“ÉS‰•)ÓTHø$HEHDT,217.62,T*1F on /dev/nmea 05.12.2014-08:46:20 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:276)> + Not receiving valid NMEA data or cut off string: ¦L“ÉS‰&# +149;)ÓTHø$HEHDT,217.62,T*1F ! 05.12.2014-08:46:20 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving $HEHDT,217.62,T*1F on /dev/nmea 05.12.2014-08:46:20 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:292)> + $VAR1 = bless( { '_CKS_' => '1F', 'TrueHeading' => '217.62', '_STRING_' => '$HEHDT,217.62,T*1F', '_calcCKS_' => '1F', '_ELEMENT_' => 'HDT', 'HDTT' => 'T', '_ERROR_' => undef, '_DEVICE_' => 'HE' }, 'NMEAdata' ); 05.12.2014-08:46:20 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:295)> + Received true heading: 217.62 ----------------------------------------------------- Heading ACU: FE0080DA000000000000 Heading degrees: 217.62
What I expect:
05.12.2014-08:43:02 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving $HEHDT,123.47,T*1C on /dev/nmea 05.12.2014-08:43:02 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:292)> + $VAR1 = bless( { '_CKS_' => '1C', 'TrueHeading' => '123.47', '_STRING_' => '$HEHDT,123.47,T*1C', '_calcCKS_' => '1C', '_ELEMENT_' => 'HDT', 'HDTT' => 'T', '_ERROR_' => undef, '_DEVICE_' => 'HE' }, 'NMEAdata' ); 05.12.2014-08:43:02 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:295)> + Received true heading: 123.47 ----------------------------------------------------- 05.12.2014-08:43:04 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving $HEHDT,123.48,T*13 on /dev/nmea 05.12.2014-08:43:04 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:292)> + $VAR1 = bless( { '_CKS_' => '13', 'TrueHeading' => '123.48', '_STRING_' => '$HEHDT,123.48,T*13', '_calcCKS_' => '13', '_ELEMENT_' => 'HDT', 'HDTT' => 'T', '_ERROR_' => undef, '_DEVICE_' => 'HE' }, 'NMEAdata' ); 05.12.2014-08:43:04 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:295)> + Received true heading: 123.48 ----------------------------------------------------- Heading ACU: FE00807B000000000000 Heading degrees: 123.48

If there's more strings involved it just will take longer when there is only one dataset per second.

So I would expect that I don't have to retie the serial port and not to get the first "bad" line all the time.

If there's another way I could get rid of the filehandle I would be open for it. Important is just that I can use something similar to readline() to process all the strings I need. Like GPS and Heading for instance:

$GPRMC,183729,A,3907.356,N,12102.482,W,000.0,360.0,080301,015.5,E*6F $GPRMB,A,,,,,,,,,,,,V*71 $GPGGA,183730,3907.356,N,12102.482,W,1,05,1.6,646.4,M,-24.1,M,,*75 $GPGSA,A,3,02,,,07,,09,24,26,,,,,1.6,1.6,1.0*3D $GPGSV,2,1,08,02,43,088,38,04,42,145,00,05,11,291,00,07,60,043,35*71 $GPGSV,2,2,08,08,02,145,00,09,46,303,47,24,16,178,32,26,18,231,43*77 $PGRME,22.0,M,52.9,M,51.0,M*14 $GPGLL,3907.360,N,12102.481,W,183730,A*33 $GPHDM,093.8,M*2B $PGRMZ,2062,f,3*2D $PGRMM,WGS 84*06 $GPBOD,,T,,M,,*47 $GPRTE,1,1,c,0*07 $GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67 $GPRMB,A,,,,,,,,,,,,V*71
I need to go through all lines to find $GPRMC and $GPHDM which is what I want.

Thanks for your help. It's really appreciated.

Replies are listed 'Best First'.
Re^3: serial port tied filehandle autoflush
by Anonymous Monk on Dec 05, 2014 at 13:51 UTC

    Thanks for the additional information. To get the most recent line, there are the three previously mentioned approaches:

    1. Keep the port closed until you actually want to read. Advantage is that there is no CPU used while you don't want to read the port. Disadvantage is that the first line you read will very likely be partial, but since NMEA is checksummed you can identify and discard bad lines (or just always discard the first line). Since you want to read the port less than once per second, I wouldn't worry too much about the overhead of re-opening the port (unless it becomes a measurable issue).
    2. Continually read the port and remember the most recent line. Advantage is that you have full control over buffering and which line you keep and return to your program, and you won't get partial lines except for the very first one. Disadvantage is that some extra CPU is used to read and discard the unnecessary lines.
    3. Keep the port open, but clear the input buffers before reading. Not having used the tied interface before, I can't say for certain, but from the docs it sounds like Device::SerialPort's lookclear method might do what you want. Note that if the buffers are not line-based but byte-based, then after clearing the buffers it's very likely you'd get partial lines like in the first approach above. Another possible disadvantage to this method is that it's more wasteful than the first if you only read rarely, plus I don't know off the top of my head what the driver's behavior is if you let the input buffers fill up.
    So I would expect that I don't have to retie the serial port and not to get the first "bad" line all the time.

    In that case that limits you to the second or third approaches. (Although personally I don't mind getting and discarding garbled first lines when opening a serial port - that's always a side effect of receiving a continuous stream of data on a serial port.)

    My suggestion would be that (if you have the time for it) you could try out the third approach, and if that does turn out to have issues, go with the first or second approach depending on how often you want to read from the port (if often, I'd go with the second, if less often, the first).