Search code examples
c++fileioread-writespc

How can I read a .spc file from C++?


I have PKCS #7 Certificates (.spc) file generated from some equipment. It has an array of floats, that I need to read into my C++ program.

Before, I was using an external program to generate a .csv file. However, I lose some precision here since it will only save 15 digits. This is a problem, I need the precision.

I have searched around for some time, but to no avail. Trying either fstream or a boost mmap just yields some encrypted gibberish...

Like this:

Fstream:

fstream iofile; 
string path = "C:\\test.spc";
iofile.open(path.c_str());
if (iofile.is_open()) {
    string s;
    while (getline(iofile, s, '\n'))
        cout << s << endl;
}

boost mmap:

boost::iostreams::mapped_file mmap("C:\\test.spc", boost::iostreams::mapped_file::readonly); // create RAM access mmap
auto f = mmap.const_data(); // set data to char array
auto l = f + mmap.size(); // used to detect end of file
string next = ""; // used to read in chars

for (; f && f != l; f++) {
    cout << f[0] << endl;
}

Both just output random characters that make no sense.

Found out it is a binary format. So I tried this:

streampos size;
char * memblock;

ifstream file("C:\\test.spc", ios::in | ios::binary | ios::ate);
if (file.is_open())
{
    size = file.tellg();
    memblock = new char[size];
    file.seekg(0, ios::beg);
    file.read(memblock, size);
    file.close();
    for (unsigned int i = 0; i < size; i++)
        cout << memblock[i];
    cout << endl << "DONE" << endl;
    cout << "the entire file content is in memory";

    delete[] memblock;
}
else cout << "Unable to open file";

Which gave alot more characters than before, but still random.

Here is a link to the file: https://drive.google.com/a/uci.edu/file/d/0B3LD-8zOiOdza2FGSVNtbnlSVjQ/view?usp=sharing


Solution

  • SOLVED. Since it is a binary file, I have been trying to find the format. Found it, I posted a link with examples files, code, and the pdf explaining the format for anyone who needs it. Here is some code in c++ I wrote that reads it in. Thanks to Reticulated Spline for the help! https://drive.google.com/a/uci.edu/folderview?id=0B3LD-8zOiOdzfjY3YXJEdGlTZ2Z1ekJGNVlJalpYRmRkOHFFaFI4XzZEaWpFbldLSEt3LW8&usp=sharing

    #include<stdio.h>
    #include <iostream>
    #include <iomanip>
    #include <map>
    using namespace std;
    
    //reads in SPC files according to "new" spc format. Advised to read pdf file, see link below
    //This shows how everything is set up, may not be entirely accurate depending on your implementation.
    //You can skip memory blocks to make codes shorter, this just shows you what each value represents.
    //See PDF for more details on each variable. https://drive.google.com/a/uci.edu/folderview?id=0B3LD-8zOiOdzfjY3YXJEdGlTZ2Z1ekJGNVlJalpYRmRkOHFFaFI4XzZEaWpFbldLSEt3LW8&usp=sharing
    //  Also includes example spc file
    //Code is as is, I am not liable for any mishaps.
    //Open source, do whatever you want with it
    //Email for questions: [email protected]
    
    int main()
    {
    FILE *ptr_myfile;
    
    // open file
    ptr_myfile = fopen("c:\\testspc.spc", "rb"); // "rb" to read binary, use "wb" to write binary
    if (!ptr_myfile) {
        printf("Unable to open file!");
        return 1;
    }
    
    // variables used to store different mem sizes
    int i = 0;              // int, 4 bytes
    unsigned char b = 'a';  // byte, 1 byte
    double d = 0;           // double, 8 bytes
    float f = 0;            // float, 4 bytes
    short int w = 0;        // word, 2 bytes
    
    // variables we actually need
    int power2 = 0; // we use this to multiply to the y value (integer data value representing intensity) later
    int numDataPoints = 0; // used to divide first and last x coord, for increments in x values (wavenumbers)
    double firstXCoord = 0; // first logged x value (wavenumber)
    double lastXCoord = 0; // last logged x value (Wavenumber
    int numSubFiles = 1;
    
    // keep track of data
    // map< subfile#, map< wavenumber, intensity> > data
    map<int, map< double, long double> > data;
    
    // start main folder
    fread(&b, sizeof(b), 1, ptr_myfile); // flags represent different things (see pdf)
    fread(&b, sizeof(b), 1, ptr_myfile); // spc file version
    fread(&b, sizeof(b), 1, ptr_myfile); // experiment type code
    fread(&b, sizeof(b), 1, ptr_myfile); // IMPORTANT exponenet for Y values 
        power2 = (int)b; // save our exponent for multiplying
    fread(&i, sizeof(i), 1, ptr_myfile); // IMPORTANT number of points in file
        numDataPoints = i; // keep this to divide my min and max x values
    fread(&d, sizeof(d), 1, ptr_myfile); // IMPORTANT first x coordinate
        firstXCoord = d; // logs first x value (wavenumber)
    fread(&d, sizeof(d), 1, ptr_myfile); // IMPORTANT last x coordinate
        lastXCoord = d; // logs last x value (wavenumber)
    fread(&i, sizeof(i), 1, ptr_myfile); // IMPORTANT Number of subfiles 
        numSubFiles = i; // keep track of how man spectra are being kept track of in this binary file
    fread(&b, sizeof(b), 1, ptr_myfile); // X units type code
    fread(&b, sizeof(b), 1, ptr_myfile); // Y units type code
    fread(&b, sizeof(b), 1, ptr_myfile); // Z units type code
    fread(&b, sizeof(b), 1, ptr_myfile); // Posting disposition
    fread(&i, sizeof(i), 1, ptr_myfile); // compressed date (see pdf for format)
    for (unsigned int j = 0; j < 9; j++) // resolution description text
        fread(&b, sizeof(b), 1, ptr_myfile);
    for (unsigned int j = 0; j < 9; j++) // source instrument description text
        fread(&b, sizeof(b), 1, ptr_myfile);
    fread(&w, sizeof(w), 1, ptr_myfile); // peak point number for interferograms
    for (unsigned int j = 0; j < 8; j++) // spare
        fread(&f, sizeof(f), 1, ptr_myfile);
    for (unsigned int j = 0; j < 130; j++) // Memo
        fread(&b, sizeof(b), 1, ptr_myfile);
    for (unsigned int j = 0; j < 30; j++)  // x, y, and z custom axis strings (combined)
        fread(&b, sizeof(b), 1, ptr_myfile);
    fread(&i, sizeof(i), 1, ptr_myfile); // byte offset to log block
    fread(&i, sizeof(i), 1, ptr_myfile); // file modification flag
    fread(&b, sizeof(b), 1, ptr_myfile); // processing code
    fread(&b, sizeof(b), 1, ptr_myfile); // calibration level + 1
    fread(&w, sizeof(w), 1, ptr_myfile); // sub method sample injection number
    fread(&f, sizeof(f), 1, ptr_myfile); // floatind data multiplier concentration factor
    for (unsigned int j = 0; j < 48; j++)  // method file
        fread(&b, sizeof(b), 1, ptr_myfile);
    fread(&f, sizeof(f), 1, ptr_myfile); // Z subfile increment for even Z multifiles
    fread(&i, sizeof(i), 1, ptr_myfile); // number of w planes
    fread(&f, sizeof(f), 1, ptr_myfile); // w plane increment
    fread(&b, sizeof(b), 1, ptr_myfile); // w axis units code
    for (unsigned int j = 0; j < 187; j++) // reserved
        fread(&b, sizeof(b), 1, ptr_myfile);
    // end main header
    
    // do this for all subfiles
    for (unsigned int subFile = 0; subFile < numSubFiles; subFile++) {
        // start sub folder for file (Even if only one file here)
        fread(&b, sizeof(b), 1, ptr_myfile); // subfiles flags (See pdf)
        fread(&b, sizeof(b), 1, ptr_myfile); // exponenet for sufiles y values
        if ((int)b != 0) // my files at least had this area blank sinc had only one sub file
            power2 = (int)b; // multiple sub files may have his changed, make sure to check other values for similar things
        fread(&w, sizeof(w), 1, ptr_myfile); // subfile index number
        fread(&f, sizeof(f), 1, ptr_myfile); // subfiels starting z value
        fread(&f, sizeof(f), 1, ptr_myfile); // subfiles ending z value
        fread(&f, sizeof(f), 1, ptr_myfile); // subfiles noise value to use peak picking
        fread(&i, sizeof(i), 1, ptr_myfile); // number of points if XYXY multifile
        fread(&i, sizeof(i), 1, ptr_myfile); // number of co-added scans
        fread(&f, sizeof(f), 1, ptr_myfile); // w axis value
        for (unsigned int j = 0; j < 4; j++) // reserved
            fread(&b, sizeof(b), 1, ptr_myfile);
        // end sub header for file
    
        // get increment if just lists y values, and not in XY format
        double increment = (lastXCoord - firstXCoord) / (numDataPoints-1);
        double waveNumber = firstXCoord;
    
        // start data entry for only x values
        for (unsigned int j = 0; j < numDataPoints; j++) {
            fread(&i, sizeof(i), 1, ptr_myfile); // read in data value
            long double intensity = i * pow(2, power2) / (pow(2, 32)); // use pow(2, 16) in bottom fraction instead if data stored as 16-bit rather than 32-bit
            data[subFile][waveNumber] = intensity; // store intensity
            if ( j <= 5)
            cout << waveNumber << " = " << intensity << endl;
            waveNumber += increment; // add increment to x value
        }
        // end data for x values
    }
    
    fclose(ptr_myfile);
    cout << "SPC FILE READ" << endl;
    int k;
    cin >> k;
    return 0;
    }