All Downloads are FREE. Search and download functionalities are using the official Maven repository.

decodes.decoder.NosHourly Maven / Gradle / Ivy

Go to download

A collection of software for aggregatting and processing environmental data such as from NOAA GOES satellites.

The newest version!
/*
* $Id$
* 
* $Log$
*
* 2023/02/21 Baoyu Yin
* Adding raw messages to the printout messages - ING 633
*
* 2023/02/15 Baoyu Yin
* Fixing issues with twos complement and undefined number - ING629 & ING 631
*
* 2023/02/08 Baoyu Yin
* Change L1 decoding (ING-625)
*
* 2023/01/09 Baoyu Yin
* Comment out 1 hour time shift for Redundant Paroscientific #2 (ING-615)
*
* Revision 1.2  2014/05/28 13:09:26  mmaloney
* dev
*
* Revision 1.1.1.1  2014/05/19 15:28:59  mmaloney
* OPENDCS 6.0 Initial Checkin
*
* Revision 1.3  2011/09/27 01:24:48  mmaloney
* Enhancements for SHEF and NOS Decoding.
*
* Revision 1.2  2011/09/21 18:27:08  mmaloney
* Updates to NOS decoding.
*
* Revision 1.1  2011/08/26 19:49:34  mmaloney
* Implemented the NOS decoders.
*
*/
package decodes.decoder;

import java.util.Calendar;
import java.util.Date;

import ilex.util.Logger;
import ilex.var.Variable;
import decodes.db.DecodesScript;
import decodes.db.Platform;
import decodes.db.PlatformConfig;
import decodes.decoder.DataOperations;
import decodes.decoder.DecodedMessage;
import decodes.decoder.DecodesFunction;
import decodes.datasource.RawMessage;

/** Handles NOS Hourly message format. */
public class NosHourly
        extends NosDecoder 
{
        public static final String module = "NosHourly";

        public NosHourly()
        {
                super();
        }

        public DecodesFunction makeCopy()
        {
                return new NosHourly();
        }

        public String getFuncName() 
        { 
                return module; 
        }

        /**
         * No arguments expected for NOS 6 Min
         */
        public void setArguments(String argString, DecodesScript script)
        {
        }

        public void execute(DataOperations dd, DecodedMessage msg)
                throws DecoderException
        {
                dataOps = dd;

                Platform platform = msg.getPlatform();
                if (platform == null)
                        throw new DecoderException(module + " function cannot be called with null platform.");
                PlatformConfig config = platform.getConfig();
                if (config == null)
                        throw new DecoderException(module + " function cannot be called with null config.");

                char msgType = (char)dd.curByte();
                if (msgType != 'N') 
                {
                        RawMessage ErrRawM = msg.getRawMessage();
                        byte[] ErrRawData = ErrRawM.getData();
                        String RawDataString = new String(ErrRawData);
                        System.out.println("The following Hourly Raw Message may have an issue: " + RawDataString);

                        throw new DecoderException(module + " requires 'N' in first char.");
                }
                dd.forwardspace();

                byte field[] = dd.getField(7, null);
                String stationId = new String(field);
                msg.getRawMessage().setPM(PM_STATION_ID, new Variable(stationId));

                dcpNum = (int)((char)dd.curByte()) - (int)'0';
                dd.forwardspace();
                msg.getRawMessage().setPM(PM_DCP_NUM, new Variable(dcpNum));

                Logger.instance().info(module + ": stationId=" + stationId + ", dcpNum=" + dcpNum);

                double datumOffset = getDouble(3, false);
                if ((int)datumOffset == 262143) datumOffset = 999999;
                msg.getRawMessage().setPM(PM_DATUM_OFFSET, new Variable (datumOffset));
                double sensorOffset = getDouble(2, true);
                if ((int)sensorOffset == 2047) sensorOffset = 999999;
                msg.getRawMessage().setPM(PM_SENSOR_OFFSET, new Variable (sensorOffset));
                int systemStatus = getInt(2, false);
                msg.getRawMessage().setPM(PM_SYSTEM_STATUS, new Variable(systemStatus));

                // Skip obsolete fields RSTS, CHKSM
                for(int i=0; i<3; i++) dd.forwardspace();

                // Time offset 1 byte
                int timeOffset = getInt(1, false);
                Logger.instance().info(module + ": dataumOffset=" + datumOffset + 
                        ", sensorOffset=" + sensorOffset + ", timeOffset=" + timeOffset);
                boolean haveBV = false;

                initSensorIndeces();

                int x, y, z, wl, sigma, outlier, offset;
                Date dataTime = null;
                Variable v;
                int sensorNum = -1; // Current water-level sensor num
                int ancSensor = -1; // Current ancillary sensor num
                char c2;
                boolean firstTime = true;

                while(dd.moreChars())
                {
                        char flag = (char)dd.curByte();
                        Logger.instance().debug3(module + " flag=" + flag);
                        dd.forwardspace();
                        switch(flag)
                        {
                        case '0': // time tag
                                x = getInt(2, false);
                                setDayNumber(x);
                                y = getInt(1, false);
                                cal.set(Calendar.HOUR_OF_DAY, y);
                                cal.set(Calendar.MINUTE, timeOffset);
                                cal.set(Calendar.SECOND, 0);
                                dataTime = cal.getTime();
                                if (firstTime)
                                {
                                        msg.getRawMessage().setPM(PM_STATION_TIME, new Variable(dataTime));
                                        firstTime = false;
                                        Logger.instance().info(module + " Set station time to " + cal.getTime());
                                }
                                Logger.instance().info(module + " Set calendar to " + cal.getTime());
                                break;
                        case '1': // PWL Aqua Track = Acousitic Water Level
                        case '(': // Air Gap
                                sensorNum = getSensorNumber(
                                        flag == '1' ? 'A' : 
                                        flag == '(' ? 'Q' : 'A', config);
                                if (sensorNum == -1)
                                        continue;
                                cal.setTime(dataTime);
                                offset = getInt(1, false);
                                for(int i=0; i<10; i++)
                                {
                                        wl = offset*250 + getInt(2, false);
                                        sigma = getInt(2, false);
                                        outlier = getInt(1, false);
                                        x = getInt(2, true); // AQT1
                                        y = getInt(2, true); // AQT2
                                        v = new Variable("" + wl + "," + sigma + "," + outlier
                                                + "," + x + "," + y);
                                        msg.addSampleWithTime(sensorNum, v, cal.getTime(), 1);
                                        cal.add(Calendar.MINUTE, -6);
                                }
                                cal.setTime(dataTime);
                                break;
                        case '2': // BWL
                                sensorNum = getSensorNumber('B', config);
                                if (sensorNum == -1)
                                        continue;
                                offset = getInt(1, false);
                                cal.setTime(dataTime);
                                for(int i=0; i<10; i++)
                                {
                                        msg.addSampleWithTime(sensorNum, 
                                                new Variable(offset*250 + getInt(2, false)), 
                                                cal.getTime(), 1);
                                        cal.add(Calendar.MINUTE, -6);
                                }
                                cal.setTime(dataTime);
                                break;

                        // The redundant blocks are all handled the same. We have
                        // a 3-byte WL sample for 6 minutes ago for the sensor we
                        // just parsed.
                        case '>': // RWL (redundant for Aqua Track)
                        case '"': // RBWL (redundant Backup
                        case '.': // Redundant SAE = value from 6 min ago.
                        case '#': // Redundant MWWL
                        case ')': // Redundant Air Gap
                        case '\'': // Redundant Paroscientific #1
                        case '*': // Redundant Paroscientific #2
                                offset = getInt(1, false);
                                cal.setTime(dataTime);
                                //cal.add(Calendar.HOUR_OF_DAY, -1);
                                for(int i=0; i<10; i++)
                                {
                                        v = new Variable(offset*250 + getInt(2, false));
                                        v.setFlags(v.getFlags() | NosDecoder.FLAG_REDUNDANT);
                                        msg.addSampleWithTime(sensorNum, v, cal.getTime(), 1);
                                        cal.add(Calendar.MINUTE, -6);
                                }
                                cal.setTime(dataTime);
                                break;
                        case '3': // Wind speed, dir, gust
                                ancSensor = getSensorNumber('C', config);
                                if (ancSensor == -1)
                                        continue;
                                cal.setTime(dataTime);

                                // Get the first wind value
                                x = getInt(2, false); // speed
                                y = getInt(2, false); // dir
                                z = getInt(2, false); // gust
/*
                                if (x == 4095) x = 999999;
                                if (y == 4095) y = 999999;
                                if (z == 4095) z = 999999;
*/
                                v = new Variable("" + x + "," + y + "," + z);
                                // Determine if 1 or 10 values are present by checking for a
                                // flag where the 2nd wind sample would start.
                                if (dd.curByte() < (int)'?') // all flags are below '?'
                                {
                                        // Only one sample present, time is top of hour
                                        cal.set(Calendar.MINUTE, 0);
                                        msg.addSampleWithTime(ancSensor, v, cal.getTime(), 1);
                                }
                                else // 10 samples, timetag as per WL
                                {
                                        msg.addSampleWithTime(ancSensor, v, cal.getTime(), 1);
                                        cal.add(Calendar.MINUTE, -6);
                                        for(int i=0; i<9; i++)
                                        {
                                                x = getInt(2, false); // speed
                                                y = getInt(2, false); // dir
                                                z = getInt(2, false); // gust
                                                v = new Variable("" + x + "," + y + "," + z);
                                                msg.addSampleWithTime(ancSensor, v, cal.getTime(), 1);
                                                cal.add(Calendar.MINUTE, -6);
                                        }
                                }
                                cal.setTime(dataTime);
                                break;
                        case '4': // Air temp
                                ancSensor = getSensorNumber('D', config);
                                if (ancSensor == -1)
                                        continue;
                                cal.setTime(dataTime);
                                for(int i=0; i<10; i++)
                                {
                                        v = getNumber(2, true);
                                        msg.addSampleWithTime(ancSensor, v, cal.getTime(), 1);
                                        cal.add(Calendar.MINUTE, -6);
                                }
                                break;
                        case '5': // Water Temp
                                ancSensor = getSensorNumber('E', config);
                                if (ancSensor == -1)
                                        continue;
                                getAncillary(ancSensor, cal, dataTime, true, msg, dd);
                                break;
                        case '6': // Barometric Pressure
                                ancSensor = getSensorNumber('F', config);
                                if (ancSensor == -1)
                                        continue;
                                getAncillary(ancSensor, cal, dataTime, false, msg, dd);
                                break;
//                      case '+': // Frequency #1 -- Ignore as per comment from Sudha
//                              break;
                        case '!': // Shaft Angle Encoder (SAE)
                                sensorNum = getSensorNumber('V', config);
                                if (sensorNum == -1)
                                        continue;
                                cal.setTime(dataTime);
                                offset = getInt(1, false);
                                for(int i=0; i<10; i++)
                                {
                                        wl = offset * 250 + getInt(2, false);
                                        sigma = getInt(2, false);
                                        outlier = getInt(1, false);
                                        v = new Variable("" + wl + "," + sigma + "," + outlier);
                                        msg.addSampleWithTime(sensorNum, v, cal.getTime(), 1);
                                        cal.add(Calendar.MINUTE, -6);
                                }
                                cal.setTime(dataTime);
                                break;
                        case '/': // unused flag
                                break;
                        case '7': // conductivity
                                ancSensor = getSensorNumber('G', config);
                                if (ancSensor == -1)
                                        continue;
                                getAncillary(ancSensor, cal, dataTime, false, msg, dd);
                                break;
                        case '8': // Microwave Water Level (MWWL)
                                sensorNum = getSensorNumber('Y', config);
                                if (sensorNum == -1)
                                        continue;
                                cal.setTime(dataTime);
                                offset = getInt(1, false);
                                for(int i=0; i<10; i++)
                                {
                                        wl = offset * 250 + getInt(2, false);
                                        sigma = getInt(2, false);
                                        outlier = getInt(1, false);
                                        v = new Variable("" + wl + "," + sigma + "," + outlier);
                                        msg.addSampleWithTime(sensorNum, v, cal.getTime(), 1);
                                        cal.add(Calendar.MINUTE, -6);
                                }
                                cal.setTime(dataTime);
                                break;
                        case '9': // Rel Hum
                                ancSensor = getSensorNumber('R', config);
                                if (ancSensor == -1)
                                        continue;
                                getAncillary(ancSensor, cal, dataTime, false, msg, dd);
                                break;
                        case ':': // Rainfall
                                ancSensor = getSensorNumber('J', config);
                                if (ancSensor == -1)
                                        continue;
                                getAncillary(ancSensor, cal, dataTime, false, msg, dd);
                                break;
                        case ';': // Solar Rad
                                ancSensor = getSensorNumber('K', config);
                                if (ancSensor == -1)
                                        continue;
                                getAncillary(ancSensor, cal, dataTime, false, msg, dd);
                                break;
                        case '<': // Analog #1 universally used for VB

                                ancSensor = getSensorNumber('L', config);
                                if (ancSensor == -1)
                                    continue;
                               
                                getAncillary(ancSensor, cal, dataTime, false, msg, dd);
                                // Phil says that after VB, increment DCP number and reset sensor indeces

                                dcpNum++;
                                initSensorIndeces();
                                break;
                        case '=': // Analog #2
                                ancSensor = getSensorNumber('M', config);
                                if (ancSensor == -1)
                                        continue;
                                getAncillary(ancSensor, cal, dataTime, false, msg, dd);
                                break;
                        case '%': // Paroscientific #1 - Pressure WL (N1,N2)
                                sensorNum = getSensorNumber('N', config);
                                if (sensorNum == -1)
                                        continue;
                                cal.setTime(dataTime);
                                offset = getInt(1, false);
                                for(int i=0; i<10; i++)
                                {
                                        wl = offset * 250 + getInt(2, false);
                                        sigma = getInt(2, false);
                                        outlier = getInt(1, false);
                                        v = new Variable("" + wl + "," + sigma + "," + outlier);
                                        msg.addSampleWithTime(sensorNum, v, cal.getTime(), 1);
                                        cal.add(Calendar.MINUTE, -6);
                                }
                                cal.setTime(dataTime);
                                break;
                        case '&': // Paroscientific #2 - Pressure WL (T1, T2, T3)
                                sensorNum = getSensorNumber('T', config);
                                if (sensorNum == -1)
                                        continue;
                                cal.setTime(dataTime);
                                offset = getInt(1, false);
                                for(int i=0; i<10; i++)
                                {
                                        wl = offset * 250 + getInt(2, false);
                                        sigma = getInt(2, false);
                                        outlier = getInt(1, false);
                                        v = new Variable("" + wl + "," + sigma + "," + outlier);
                                        msg.addSampleWithTime(sensorNum, v, cal.getTime(), 1);
                                        cal.add(Calendar.MINUTE, -6);
                                }
                                cal.setTime(dataTime);
                                break;
                        case ',': // Unused flag
                                break;
                        case ' ': // Delimiter for single-char VB at end of message
                                // Only try to get BV once to avoid reading past end of data.
                                if (!haveBV)
                                {
                                        v = new Variable(getDouble(1, false)+9.5);
                                        msg.getRawMessage().setPM(PM_NOS_BATTERY, v);
                                        haveBV = true;
                                }
                                break;
                        case '-': // Start of 2-char flag
                                c2 = (char)dd.curByte();
                                dd.forwardspace();
                                if (c2 == 'O') // Visibility
                                {
                                        x = getInt(3, false);
                                        y = getInt(1, false);
                                        ancSensor = getSensorNumber('O', config);
                                        if (ancSensor == -1)
                                                continue;
                                        msg.addSampleWithTime(ancSensor, 
                                                new Variable("" + x + "," + y), dataTime, 1);
                                }
                                break;
                        default:
                                if (!Character.isWhitespace(flag))
                                        Logger.instance().warning(module + " Unrecognized flag char '" + flag + "'");
                        }
                }
                Logger.instance().debug3(module + " end of message");
        }


        /**
         * Helper method to get one or 10 ancillary values.
         * @param sensorNum
         * @param cal
         * @param dataTime
         * @param signed
         * @param msg
         * @param dd
         * @throws DecoderException
         */
        private void getAncillary(int sensorNum, Calendar cal, Date dataTime,
                boolean signed, DecodedMessage msg, DataOperations dd)
                throws DecoderException
        {
                cal.setTime(dataTime);

                // Get the ancillary value
                Variable v = getNumber(2, signed);

                // Determine if 1 or 10 values are present by checking for a
                // flag where the 2nd sample would start.
                if (dd.curByte() < (int)'?') // all flags are below '?'
                {
                        // Only one sample present, time is top of hour
                        cal.set(Calendar.MINUTE, 0);
                        msg.addSampleWithTime(sensorNum, v, cal.getTime(), 1);
                }
                else // 10 samples, timetag as per WL
                {
                        msg.addSampleWithTime(sensorNum, v, cal.getTime(), 1);
                        cal.add(Calendar.MINUTE, -6);
                        for(int i=0; i<9; i++)
                        {
                                v = getNumber(2, signed);
                                msg.addSampleWithTime(sensorNum, v, cal.getTime(), 1);
                                cal.add(Calendar.MINUTE, -6);
                        }
                }
                cal.setTime(dataTime);
        }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy