Find Index of Closest Value in a Vector of Struct

I have txt file with values for temperature, voltage, and sensitivity which I am reading into a vector of a struct.

TEXT FILE SNIPPET
1
2
3
4
5
6
7
8
9

Temp.      Voltage    Sensitivity
(Kelvin)   (Volts)    (milliVolts/Kelvin)

1.4 1.644290    -12.5
1.5 1.642990    -13.6
1.6 1.641570    -14.8
1.7 1.640030    -16.0
1.8 1.638370    -17.1

VECTOR/STRUCT

1
2
3
4
5
6
7
8
9
10
struct TempResponse
{
    double kelvin;
    double mV;
    double sensitivity;
};


std::vector<TempResponse> response;

What I am trying to accomplish is converting from Kelvin to mV, and vice versa and finding the closest approximate corresponding value (e.g 1.42 K = 1644290 mV).

Here is my function that converts mV to K which works (still need to test boundaries etc).

MV TO KELVIN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

double Convert::convertmVtoK(double value) const
{
    auto it = lower_bound(response.begin(), response.end(), value,
                    [](const TempResponse& r, double v){
                        return r.mV > v; });
    int index = std::distance(response.begin(), it);

    if(value>response[0].mV || value < response.back().mV)
    {
        std::cout<<"Warning: Voltage Out of Range"<<"\n";
    }

    else if(value==response[0].mV || value==response.back().mV
            ||fabs(value - response[index].mV) <= 0.0001 * fabs(value))
    {
        std::cout<<response[index].kelvin;
        return response[index].kelvin;
    }

    else
    {
        double diff1 = std::abs(value - response[index].mV);
        double diff2 = std::abs(value - response[index-1].mV);

        if (diff2 < diff1)
        {
            std::cout<<response[index-1].kelvin;
            return response[index-1].kelvin;
        }

        else
        {
            std::cout<<response[index].kelvin;
            return response[index].kelvin;
        }
    }
}

The issue I am having is converting K to mV using a similar method, but cannot get my iterator to the correct value.

1
2
3
4
5
6
double Convert::convertKtomV(double value) const
{
    auto it = lower_bound(response.begin().kelvin, response.back().kelvin, value);
    int index = std::distance(response.begin(), it);  
 ...
}

So my question is how to define the iterator to work the same as my first function for ascending order values?
Just a guess.. How about using a reverse iterator?

Or instead of finding the first greater or equal to element, find the first lesser than element and go back one element?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <cassert>
using namespace std;

//--------------------------------------

struct TempResponse                                        // Basic data structure
{
   double kelvin;
   double mV;
   double sensitivity;
};

//--------------------------------------

istream &operator >> ( istream &in, TempResponse &tr )     // Read one data item
{ 
   double V;
   in >> tr.kelvin >> V >> tr.sensitivity;
   tr.mV = 1000 * V;   // Conversion V to mV
   return in;
}

//--------------------------------------

vector<TempResponse> getData( istream &in )                // Read data from any input stream
{
   vector<TempResponse> result;
   string line;
   TempResponse tr;

   while ( getline( in, line ) )
   {
      stringstream ss( line );
      if ( ss >> tr ) result.push_back( tr );
   }

   return result;
}

//--------------------------------------

// Interpolate (NOT use closest).
// Independent variable x, dependent variable y
// Use MEMBER POINTERS to specify these; see  https://en.cppreference.com/w/cpp/language/pointer
// NOTE: must be sorted by whatever is x

double interpolate( const vector<TempResponse> &data, double TempResponse::* x, double TempResponse::* y, double xValue )
{
   auto ip = lower_bound( data.begin(), data.end(), xValue, [ x ]( TempResponse a, double b ){ return a.*x < b; } );

   // No extrapolation beyond data limits
   if ( ip == data.end  () ) return data.back ().*y;
   if ( ip == data.begin() ) return data.front().*y;

   // Otherwise, linear interpolation
   auto im = ip - 1;
   double slope = ( (*ip).*y - (*im).*y ) / ( (*ip).*x - (*im).*x );      // dY/dX
   return (*im).*y + slope * ( xValue - (*im).*x );                       // Y0 + (dY/dX) * ( X - X0 )
}

//--------------------------------------

int main()
{
   // Get data
// ifstream in( "data.dat" );   assert( in );                             // Data file ...
   stringstream in( "                                         \n"         // ... or fudged for demonstration
                    "Temp.      Voltage    Sensitivity        \n"
                    "(Kelvin)   (Volts)    (milliVolts/Kelvin)\n"
                    "                                         \n"
                    "1.4        1.644290    -12.5             \n"
                    "1.5        1.642990    -13.6             \n"
                    "1.6        1.641570    -14.8             \n"
                    "1.7        1.640030    -16.0             \n"
                    "1.8        1.638370    -17.1             \n" );


   vector<TempResponse> data = getData( in );
   cout << "Data read" << '\n' << "T" << '\t' << "mV" << '\t' << "sensitivity (mV)" << '\n';
   for ( TempResponse tr : data ) cout << tr.kelvin << '\t' << tr.mV << '\t' << tr.sensitivity << '\n';
   cout << '\n';



   // Do some interpolation: temperature to voltage
   cout << "Temperature as independent variable" << '\n' << "T" << '\t' << "mV" << '\n';
   sort( data.begin(), data.end(), []( TempResponse a, TempResponse b ){ return a.kelvin < b.kelvin; } );

   double Tvalues[] = { 1.0, 1.425, 1.65, 1.9 };
   for ( double T : Tvalues ) cout << T << '\t' << interpolate( data, &TempResponse::kelvin, &TempResponse::mV, T ) << '\n';
   cout << '\n';



   // Do some interpolation: voltage to temperature
   cout << "milliVolts as independent variable" << '\n' << "mV" << "\t" << "T" << '\n';
   sort( data.begin(), data.end(), []( TempResponse a, TempResponse b ){ return a.mV < b.mV; } );

   double mVvalues[] = { 1630.0, 1642.0, 1648.0 };
   for ( double mV : mVvalues ) cout << mV << '\t' << interpolate( data, &TempResponse::mV, &TempResponse::kelvin, mV ) << '\n';
   cout << '\n';
}

//-------------------------------------- 


Data read
T	mV	sensitivity (mV)
1.4	1644.29	-12.5
1.5	1642.99	-13.6
1.6	1641.57	-14.8
1.7	1640.03	-16
1.8	1638.37	-17.1

Temperature as independent variable
T	mV
1	1644.29
1.425	1643.96
1.65	1640.8
1.9	1638.37

milliVolts as independent variable
mV	T
1630	1.8
1642	1.56972
1648	1.4
Last edited on
Topic archived. No new replies allowed.