diff --git a/Graphs/gGraphView.h b/Graphs/gGraphView.h index fef23196..35ab5d6f 100644 --- a/Graphs/gGraphView.h +++ b/Graphs/gGraphView.h @@ -132,12 +132,22 @@ protected: GLubyte * colors; }; +/*! \struct TextQue + \brief Holds a single item of text for the drawing queue + */ struct TextQue { - short x,y; + //! \variable contains the x axis screen position to draw the text + short x; + //! \variable contains the y axis screen position to draw the text + short y; + //! \variable the angle in degrees for drawing rotated text float angle; + //! \variable the actual text to draw QString text; + //! \variable the color the text will be drawn in QColor color; + //! \variable a pointer to the QFont to use to draw this text QFont *font; }; @@ -243,9 +253,11 @@ public: //! \brief Draw all this layers custom GLBuffers (ie. the actual OpenGL Vertices) virtual void drawGLBuf(float linesize); + //! \note not sure why I needed the reference counting stuff. short m_refcount; void addref() { m_refcount++; } bool unref() { m_refcount--; if (m_refcount<=0) return true; return false; } + protected: //! \brief Add a GLBuffer (vertex) object customized to this layer void addGLBuf(GLBuffer *buf) { mgl_buffers.push_back(buf); } @@ -262,14 +274,21 @@ protected: short m_Y; short m_order; // order for positioning.. LayerPosition m_position; + + //! \brief A vector containing all this layers custom drawing buffers QVector mgl_buffers; - // Default layer mouse handling = Do nothing + //! \brief Mouse wheel moved somewhere over this layer virtual bool wheelEvent(QWheelEvent * event) { Q_UNUSED(event); return false; } + //! \brief Mouse moved somewhere over this layer virtual bool mouseMoveEvent(QMouseEvent * event) { Q_UNUSED(event); return false; } + //! \brief Mouse left or right button pressed somewhere on this layer virtual bool mousePressEvent(QMouseEvent * event) { Q_UNUSED(event); return false; } + //! \brief Mouse button released that was originally pressed somewhere on this layer virtual bool mouseReleaseEvent(QMouseEvent * event) { Q_UNUSED(event); return false; } + //! \brief Mouse button double clicked somewhere on this layer virtual bool mouseDoubleClickEvent(QMouseEvent * event) { Q_UNUSED(event); return false; } + //! \brief A key was pressed on the keyboard while the graph area was focused. virtual bool keyPressEvent(QKeyEvent * event) { Q_UNUSED(event); return false; } }; @@ -285,9 +304,16 @@ public: //! \brief Add Layer to this Layer Group virtual void AddLayer(Layer *l); + //! \brief Returns the minimum time value for all Layers contained in this group (milliseconds since epoch) virtual qint64 Minx(); + + //! \brief Returns the maximum time value for all Layers contained in this group (milliseconds since epoch) virtual qint64 Maxx(); + + //! \brief Returns the minimum Y-axis value for all Layers contained in this group virtual EventDataType Miny(); + + //! \brief Returns the maximum Y-axis value for all Layers contained in this group virtual EventDataType Maxy(); //! \brief Check all layers contained and return true if none contain data @@ -303,9 +329,8 @@ public: QVector & getLayers() { return layers; } protected: + //! \brief Contains all Layer objects in this group QVector layers; - - //overide mouse handling to pass to sublayers.. }; class gGraph; @@ -321,7 +346,10 @@ public: gThread(gGraphView *g); ~gThread(); + //! \brief Start thread process void run(); + + //! \brief Kill thread process void die() { m_running=false; } QMutex mutex; protected: @@ -336,6 +364,7 @@ class gToolTip: public QObject { Q_OBJECT public: + //! \brief Initializes the ToolTip object, and connects the timeout to the gGraphView object gToolTip(gGraphView * graphview); virtual ~gToolTip(); @@ -372,9 +401,19 @@ class gGraph:public QObject public: friend class gGraphView; + /*! \brief Creates a new graph object + \param gGraphView * graphview if not null, links the graph to that object + \param QString title containing the graph Title which is rendered vertically + \param int height containing the opening height for this graph + \param short group containing which graph-link group this graph belongs to + */ gGraph(gGraphView * graphview=NULL, QString title="", QString units="", int height=100,short group=0); virtual ~gGraph(); + + //! \brief Tells all Layers to deselect any highlighting void deselect(); + + //! \brief Starts the singleshot Timer running, for ms milliseconds void Trigger(int ms); /*! \fn QPixmap renderPixmap(int width, int height, float fontscale=1.0); @@ -400,6 +439,7 @@ public: //! \brief Set the height element. (relative to the total of all heights) void setHeight(float height) { m_height=height; } + // Can't remember what these are for.. int minHeight() { return m_min_height; } void setMinHeight(int height) { m_min_height=height; } @@ -451,10 +491,10 @@ public: //! \brief Sets the time range selected for this graph (in milliseconds since 1970 epoch) virtual void SetXBounds(qint64 minx, qint64 maxx); - //! \brief Returns the physical Minimum time for all layers contained + //! \brief Returns the physical Minimum time for all layers contained (in milliseconds since epoch) virtual qint64 MinX(); - //! \brief Returns the physical Maximum time for all layers contained + //! \brief Returns the physical Maximum time for all layers contained (in milliseconds since epoch) virtual qint64 MaxX(); //! \brief Returns the physical Minimum Y scale value for all layers contained @@ -463,9 +503,16 @@ public: //! \brief Returns the physical Maximum Y scale value for all layers contained virtual EventDataType MaxY(); + //! \brief Sets the physical start of this graphs time range (in milliseconds since epoch) virtual void SetMinX(qint64 v); + + //! \brief Sets the physical end of this graphs time range (in milliseconds since epoch) virtual void SetMaxX(qint64 v); + + //! \brief Sets the physical Minimum Y scale value used while drawing this graph virtual void SetMinY(EventDataType v); + + //! \brief Sets the physical Maximum Y scale value used while drawing this graph virtual void SetMaxY(EventDataType v); //! \brief Forces Y Minimum to always select this value @@ -482,9 +529,12 @@ public: //! \brief Set recommended Y minimum.. It won't go above this unless the data does. It won't go under this. virtual void setRecMaxY(EventDataType v) { rec_maxy=v; } + //! \brief Returns the recommended Y minimum.. It won't go under this unless the data does. It won't go above this. virtual EventDataType RecMinY() { return rec_miny; } + //! \brief Returns the recommended Y maximum.. It won't go under this unless the data does. It won't go above this. virtual EventDataType RecMaxY() { return rec_maxy; } + //! \brief Called when main graph area is resized void resize(int width, int height); // margin recalcs.. qint64 max_x,min_x,rmax_x,rmin_x; @@ -534,9 +584,14 @@ public: m_marginleft=left; m_marginright=right; m_margintop=top; m_marginbottom=bottom; } + + //! \brief Returns this graphs left margin short marginLeft(); + //! \brief Returns this graphs right margin short marginRight(); + //! \brief Returns this graphs top margin short marginTop(); + //! \brief Returns this graphs bottom margin short marginBottom(); //! \brief Returns the main gGraphView objects GLShortBuffer line list. @@ -551,6 +606,8 @@ public: QRect m_lastbounds; QTimer * timer; + + //! \brief Returns a Vector reference containing all this graphs layers QVector & layers() { return m_layers; } short m_marginleft, m_marginright, m_margintop, m_marginbottom; @@ -558,7 +615,6 @@ protected: //void invalidate(); //! \brief Mouse Wheel events - virtual void wheelEvent(QWheelEvent * event); //! \brief Mouse Movement events @@ -638,7 +694,10 @@ public: //! \brief Add gGraph g to this gGraphView, in the requested graph-linkage group void addGraph(gGraph *g,short group=0); + //! \brief The width of the vertical text Title area for all graphs static const int titleWidth=30; + + //! \brief The splitter is drawn inside this gap static const int graphSpacer=4; //! \brief Finds the top pixel position of the supplied graph @@ -646,6 +705,8 @@ public: //! \brief Returns the scaleY value, which is used when laying out less graphs than fit on the screen. float scaleY() { return m_scaleY; } + + //! \brief Sets the scaleY value, which is used when laying out less graphs than fit on the screen. void setScaleY(float sy) { m_scaleY=sy; } //! \brief Returns the current selected time range @@ -695,12 +756,15 @@ public: //! \brief Draw all Text in the text drawing queue, via QPainter void DrawTextQue(); + //! \brief Returns number of graphs contained (whether they are visible or not) int size() { return m_graphs.size(); } //! \brief Return individual graph by index value gGraph * operator[](int i) { return m_graphs[i]; } + //! \brief Returns the custom scrollbar object linked to this gGraphArea MyScrollBar * scrollBar() { return m_scrollbar; } + //! \brief Sets the custom scrollbar object linked to this gGraphArea void setScrollBar(MyScrollBar *sb); //! \brief Calculates the correct scrollbar parameters for all visible, non empty graphs. @@ -742,6 +806,7 @@ public: //! \brief Hides the splitter, used in report printing code void hideSplitter() { m_showsplitter=false; } + //! \brief Re-enabled the in-between graph splitters. void showSplitter() { m_showsplitter=true; } @@ -770,18 +835,26 @@ protected: //! \brief Set the Vertical offset (used in scrolling) void setOffsetY(int offsetY); + //! \brief Set the Horizontal offset (not used yet) void setOffsetX(int offsetX); + //! \brief Mouse Moved somewhere in main gGraphArea, propagates to the individual graphs virtual void mouseMoveEvent(QMouseEvent * event); + //! \brief Mouse Button Press Event somewhere in main gGraphArea, propagates to the individual graphs virtual void mousePressEvent(QMouseEvent * event); + //! \brief Mouse Button Release Event somewhere in main gGraphArea, propagates to the individual graphs virtual void mouseReleaseEvent(QMouseEvent * event); + //! \brief Mouse Button Double Click Event somewhere in main gGraphArea, propagates to the individual graphs virtual void mouseDoubleClickEvent(QMouseEvent * event); + //! \brief Mouse Wheel Event somewhere in main gGraphArea, propagates to the individual graphs virtual void wheelEvent(QWheelEvent * event); + //! \brief Keyboard event while main gGraphArea has focus. virtual void keyPressEvent(QKeyEvent * event); //! \brief Add Graph to drawing queue, mainly for the benefit of multithreaded drawing code - void queGraph(gGraph *,int originX, int originY, int width, int height); // que graphs for drawing (used internally by paintGL) + void queGraph(gGraph *,int originX, int originY, int width, int height); + //! \brief the list of graphs to draw this frame QList m_drawlist; @@ -794,7 +867,11 @@ protected: //! \brief List of all graphs contained, indexed by title QHash m_graphsbytitle; - int m_offsetY,m_offsetX; // Scroll Offsets + //! \variable Vertical scroll offset (adjusted when scrollbar gets moved) + int m_offsetY; + //! \variable Horizontal scroll offset (unused, but can be made to work if necessary) + int m_offsetX; + //! \variable Scale used to enlarge graphs when less graphs than can fit on screen. float m_scaleY; bool m_sizer_dragging; @@ -811,8 +888,9 @@ protected: bool m_graph_dragging; int m_graph_index; - + //! \brief List of all queue text to draw.. not sure why I didn't use a vector here.. Might of been a leak issue TextQue m_textque[textque_max]; + int m_textque_items; int m_lastxpos,m_lastypos; diff --git a/Graphs/gLineChart.h b/Graphs/gLineChart.h index 9127c549..5696b0c3 100644 --- a/Graphs/gLineChart.h +++ b/Graphs/gLineChart.h @@ -32,22 +32,50 @@ protected: GLShortBuffer * lines; }; +/*! \class gLineChart + \brief Draws a 2D linechart from all Session data in a day. EVL_Waveforms typed EventLists are accelerated. + */ class gLineChart:public Layer { public: + /*! \brief Creates a new 2D gLineChart Layer + \param code The Channel that gets drawn by this layer + \param col Color of the Plot + \param square_plot Whether or not to use square plots (only effective for EVL_Event typed EventList data) + \param disable_accel Whether or not to disable acceleration for EVL_Waveform typed EventList data + */ gLineChart(ChannelID code,const QColor col=QColor("black"), bool square_plot=false,bool disable_accel=false); virtual ~gLineChart(); + //! \brief The drawing code that fills the vertex buffers virtual void paint(gGraph & w,int left, int top, int width, int height); + //! \brief Set Use Square plots for non EVL_Waveform data void SetSquarePlot(bool b) { m_square_plot=b; } + + //! \brief Returns true if using Square plots for non EVL_Waveform data bool GetSquarePlot() { return m_square_plot; } + + //! \brief Set this if you want this layer to draw it's empty data message + //! \note They don't show anymore due to the changes in gGraphView. Should modify isEmpty if this still gets to live void ReportEmpty(bool b) { m_report_empty=b; } + + //! \brief Returns whether or not to show the empty text message bool GetReportEmpty() { return m_report_empty; } + + //! \brief Sets the ability to Disable waveform plot acceleration (which hides unseen data) void setDisableAccel(bool b) { m_disable_accel=b; } + + //! \brief Returns true if waveform plot acceleration is disabled bool disableAccel() { return m_disable_accel; } + + //! \brief Sets the Day object containing the Sessions this linechart draws from virtual void SetDay(Day *d); + + //! \brief Returns Minimum Y-axis value for this layer virtual EventDataType Miny(); + + //! \brief Returns Maximum Y-axis value for this layer virtual EventDataType Maxy(); protected: @@ -55,10 +83,16 @@ protected: bool m_square_plot; bool m_disable_accel; QColor m_line_color; + GLShortBuffer * lines; GLShortBuffer * outlines; + + //! \brief Used by accelerated waveform plots. Must be >= Screen Resolution (or at least graph width) static const int max_drawlist_size=4096; + + //! \brief The list of screen points used for accelerated waveform plots.. QPoint m_drawlist[max_drawlist_size]; + int subtract_offset; }; diff --git a/Graphs/gSegmentChart.h b/Graphs/gSegmentChart.h index 31e6a1c4..b9399fde 100644 --- a/Graphs/gSegmentChart.h +++ b/Graphs/gSegmentChart.h @@ -11,18 +11,31 @@ enum GraphSegmentType { GST_Pie, GST_CandleStick, GST_Line }; +/*! \class gSegmentChart + \brief Draws a PieChart, CandleStick or 2D Line plots containing multiple Channel 'slices' + */ class gSegmentChart : public Layer { public: gSegmentChart(GraphSegmentType gt=GST_Pie, QColor gradient_color=Qt::white,QColor outline_color=Qt::black); virtual ~gSegmentChart(); + //! \brief The drawing code that fills the Vertex buffers virtual void paint(gGraph & w,int left, int top, int width, int height); + + //! \brief Pre-fills a buffer with the data needed to draw virtual void SetDay(Day *d); + + //! \brief Returns true if no data available for drawing virtual bool isEmpty(); + //! \brief Adds a channel slice, and sets the color and label void AddSlice(ChannelID code,QColor col,QString name=""); + + //! \brief Sets the fade-out color to make the graphs look more attractive void setGradientColor(QColor & color) { m_gradient_color=color; } + + //! \brief Sets the outline color for the edges drawn around the Pie slices void setOutlineColor(QColor & color) { m_outline_color=color; } const GraphSegmentType & graphType() { return m_graph_type; } void setGraphType(GraphSegmentType type) { m_graph_type=type; } @@ -41,6 +54,10 @@ protected: GLFloatBuffer *poly,*lines; }; +/*! \class gTAPGraph + \brief Time at Pressure chart, derived from gSegmentChart + \notes Currently unused + */ class gTAPGraph:public gSegmentChart { public: diff --git a/Graphs/gSummaryChart.h b/Graphs/gSummaryChart.h index 2a1d5a15..fa909988 100644 --- a/Graphs/gSummaryChart.h +++ b/Graphs/gSummaryChart.h @@ -11,22 +11,44 @@ #include "gGraphView.h" #include "gXAxis.h" +/*! \enum GraphType + \value GT_BAR Display as a BarGraph + \value GT_LINE Display as a line plot + \value GT_SESSIONS Display type for session times chart + */ enum GraphType { GT_BAR, GT_LINE, GT_SESSIONS }; +/*! \class SummaryChart + \brief The main overall chart type layer used in Overview page + */ class SummaryChart:public Layer { public: + //! \brief Constructs a SummaryChart with QString label, of GraphType type SummaryChart(QString label, GraphType type=GT_BAR); virtual ~SummaryChart(); + //! \brief Drawing code that fills the Vertex buffers virtual void paint(gGraph & w,int left, int top, int width, int height); + + //! \brief Precalculation code prior to drawing. Day object is not needed here, it's just here for Layer compatability. virtual void SetDay(Day * day=NULL); + + //! \brief Returns true if no data was found for this day during SetDay virtual bool isEmpty() { return m_empty; } + + //! \brief Adds a layer to the summaryChart (When in Bar mode, it becomes culminative, eg, the AHI chart) void addSlice(ChannelID code, QColor color, SummaryType type, bool ignore_zeros) { m_codes.push_back(code); m_colors.push_back(color); m_type.push_back(type); m_zeros.push_back(ignore_zeros); } + + //! \brief Deselect highlighting (the gold bar) virtual void deselect() { hl_day=-1; } + + //! \brief Sets the MachineType this SummaryChart is interested in void setMachineType(MachineType type) { m_machinetype=type; } + + //! \brief Returns the MachineType this SummaryChart is interested in MachineType machineType() { return m_machinetype; } protected: Qt::Orientation m_orientation; @@ -59,9 +81,16 @@ class SummaryChart:public Layer int tz_offset; float tz_hours; + //! \brief Key was pressed that effects this layer virtual bool keyPressEvent(QKeyEvent * event); + + //! \brief Mouse moved over this layers area (shows the hover-over tooltips here) virtual bool mouseMoveEvent(QMouseEvent * event); + + //! \brief Mouse Button was pressed over this area virtual bool mousePressEvent(QMouseEvent * event); + + //! \brief Mouse Button was released over this area. (jumps to daily view here) virtual bool mouseReleaseEvent(QMouseEvent * event); }; diff --git a/Graphs/glcommon.h b/Graphs/glcommon.h index 92b4cf92..509e3dc8 100644 --- a/Graphs/glcommon.h +++ b/Graphs/glcommon.h @@ -10,10 +10,21 @@ #include #include +/*! \brief Draw an outline of a rounded rectangle + \param radius Radius of corner rounding + \param lw Line Width + \param color Color of drawn lines + */ void LinedRoundedRectangle(int x,int y,int w,int h,int radius,int lw,QColor color); + +/*! \brief Draws a filled rounded rectangle + \param radius Radius of corner rounding + \param color Color of entire rectangle + */ void RoundedRectangle(int x,int y,int w,int h,int radius,const QColor color); #ifdef BUILD_WITH_MSVC +// Visual C++ doesn't have either of these in it's maths header.. I'm not surprised at Microsofts maths abilities.. const double M_PI=3.141592653589793; double round(double number); diff --git a/SleepLib/preferences.h b/SleepLib/preferences.h index 602bb4a2..9ab75d3c 100644 --- a/SleepLib/preferences.h +++ b/SleepLib/preferences.h @@ -28,70 +28,91 @@ inline QString PrefMacro(QString s) return "{"+s+"}"; } +//! \brief Returns a QString containing the Username, according to the Operating System const QString & getUserName(); + +/*! \class Preferences + \author Mark Watkins + \brief Holds a group of preference variables + */ class Preferences { public: + //! \brief Constructs a Preferences object 'name', and remembers sets the filename Preferences(QString name,QString filename=""); Preferences(); virtual ~Preferences(); + //! \brief Returns a QString containing preference 'name', processing any {} macros const QString Get(QString name); - //const QString Get(const char * name) { -// return Get(name); - // }; - /*const QString Get(int code) { - return Get(p_codes[code]); - }*/ - - // operator[] will not expand {} macros + //! \brief Returns the QVariant value of the selected preference.. Note, preference must exist, and will not expand {} macros QVariant & operator[](QString name) { return p_preferences[name]; } - /*QVariant & operator[](int code) { - return p_preferences[p_codes[code]]; - }*/ + //! \brief Sets the Preference 'name' to QVariant 'value' void Set(QString name,QVariant value) { p_preferences[name]=value; } - /*void Set(int code,QVariant value) { - Set(p_codes[code],value); - }*/ + //! \brief Returns true if preference 'name' exists bool Exists(QString name) { return (p_preferences.contains(name)); } + + //! \brief Returns true if preference 'name' exists, and contains a boolean true value bool ExistsAndTrue(QString name) { QHash::iterator i=p_preferences.find(name); if (i==p_preferences.end()) return false; return i.value().toBool(); } + + //! \brief Removes preference 'name' from this Preferences group void Erase(QString name) { QHash::iterator i=p_preferences.find(name); if (i!=p_preferences.end()) p_preferences.erase(i); } + //! \brief Derive from this to handle Loading of any custom XML sections virtual void ExtraLoad(QDomElement & root) { root=root; } + + //! \brief Derive from this to handle Saving of any custom XML sections + //! \return Must return a QDomElement to be inserted into the generated XML virtual QDomElement ExtraSave(QDomDocument & doc) { doc=doc; QDomElement e; return e; } + //! \brief Opens, processes the XML for this Preferences group, loading all preferences stored therein. + //! \note If filename is empty, it will use the one specified in the constructor + //! \returns true if succesful virtual bool Open(QString filename=""); + + //! \brief Saves all preferences to XML file. + //! \note If filename is empty, it will use the one specified in the constructor + //! \returns true if succesful virtual bool Save(QString filename=""); + //! \note Sets a comment string whici will be stored in the XML void SetComment(const QString & str) { p_comment=str; } + //! \brief Finds a given preference. + //! \returns a QHash::iterator pointing to the preference named 'key', or an empty end() iterator inline QHash::iterator find(QString key) { return p_preferences.find(key); } + + //! \brief Returns an empty iterator pointing to the end of the preferences list inline QHash::iterator end() { return p_preferences.end(); } + + //! \brief Returns an iterator pointing to the first item in the preferences list inline QHash::iterator begin() { return p_preferences.begin(); } //int GetCode(QString name); // For registering/looking up new preference code. + //! \brief Stores all the variants indexed by a QString name for this Preferences object QHash p_preferences; + protected: //QHash p_codes; QString p_comment; @@ -101,6 +122,11 @@ protected: }; enum PrefType { PT_Checkbox, PT_Integer, PT_Number, PT_Date, PT_Time, PT_DateTime, PT_LineEdit, PT_TextEdit, PT_Dropdown }; + +/*! \class Preference + \brief Holds a single preference + \note This is a work in progress to clean up preferences system + */ class Preference { public: @@ -138,7 +164,10 @@ protected: Q_DECLARE_METATYPE(Preference) +//! \brief Main Preferences Object used throughout the application extern Preferences PREF; + +//! \brief Layout Preferences Object used throughout the application extern Preferences LAYOUT; #endif // PREFERENCES_H diff --git a/SleepLib/profiles.h b/SleepLib/profiles.h index e3bb6e2b..d2f06671 100644 --- a/SleepLib/profiles.h +++ b/SleepLib/profiles.h @@ -16,17 +16,20 @@ License: GPL #include "preferences.h" class Machine; + /*! \class Profile \author Mark Watkins \date 28/04/11 - \file profiles.h - \brief User profile system + \brief The User profile system, containing all information for a user, and an index into all Machine data */ class Profile:public Preferences { public: + //! \brief Creates a new Profile object 'name' (which is usually just set to "Profile", the XML filename is derived from this) Profile(QString name); + + //! \brief Create a new empty Profile object Profile(); virtual ~Profile(); @@ -51,7 +54,7 @@ public: void DataFormatError(Machine *m); /*! \brief Import Machine Data - \param path + \param path containing import location */ int Import(QString path); diff --git a/oximetry.cpp b/oximetry.cpp index 03bcac73..de978701 100644 --- a/oximetry.cpp +++ b/oximetry.cpp @@ -433,7 +433,7 @@ void CMS50Serial::import_process() return; } qDebug() << "CMS50 import complete. Processing" << data.size() << "bytes"; - unsigned char a,pl,o2,lastpl=0,lasto2=0; + unsigned short a,pl,o2,lastpl=0,lasto2=0; int i=0; int size=data.size(); @@ -452,9 +452,8 @@ void CMS50Serial::import_process() qint64 lastpltime=0,lasto2time=0; bool first=true; while (i<(size-3)) { - a=data.at(i++); - Q_UNUSED(a); - pl=data.at(i++) ^ 0x80; + a=data.at(i++); // low bits are supposedly the high bits of the heart rate + pl=(data.at(i++) & 0x7f) | ((a & 3) << 7); o2=data.at(i++); if (pl!=0) { if (lastpl!=pl) { @@ -571,8 +570,8 @@ void CMS50Serial::ReadyRead() static int lastbytesize=0; int size=bytes.size(); // Process all incoming serial data packets - unsigned char c; - unsigned char pl,o2; + unsigned short c; + unsigned short pl,o2; if (!import_mode) { QString data="Read: "; @@ -679,14 +678,17 @@ void CMS50Serial::ReadyRead() //read data blocks.. } } else { + static unsigned short hb=0; if (bytes[i]&0x80) { // 0x80 == sync bit EventDataType d=bytes[i+1] & 0x7f; + hb=(bytes[i+2] & 0x40) << 1; + addPlethy(lasttime,d); lasttime+=20; i+=3; } else { - pl=bytes[i]; + pl=(bytes[i] & 0x7f) | hb; o2=bytes[i+1]; addPulse(lasttime,pl); addSpO2(lasttime,o2);