简体   繁体   English

使用Core-Plot的实时图上未说明的水平线

[英]Unaccounted horizontal lines on real time plot using Core-Plot

I am trying to draw a real time plot of a 4-channel input stream using 4 separate plots on the same graph. 我试图在同一张图上使用4个单独的图绘制4通道输入流的实时图。 Currently I am taking this data out of a text file, but will eventually work to get this data using bluetooth. 目前,我正在从文本文件中提取此数据,但最终将使用蓝牙来获取此数据。 My plot itself works fine, but core-plot appears to draw an extra horizontal line for each plot at the beginning of the plotting at the levels of the first data set. 我的图本身工作正常,但核心图似乎在图的开头在第一个数据集的级别为每个图绘制了一条额外的水平线。 I think this has something to do with core-plot trying to get all the points to be plotted before it starts to display them. 我认为这与核心图有关,它试图在开始显示它们之前获取所有要绘制的点。 The following image shows these lines, while the actual waveforms, square waves, are in the middle of being plot. 下图显示了这些线,而实际波形(方波)正处于绘图的中间。

在此处输入图片说明

As the plotting progresses and the displayed range of the x-axis changes, these lines disappear. 随着绘图的进行和x轴显示范围的变化,这些线会消失。 Like in here: 就像在这里:

在此处输入图片说明

Following is the relevant section of my code. 以下是我的代码的相关部分。 It is based on Ray Wenderlich's scatter plot tutorial and Eric Skrooch's real time plot example. 它基于Ray Wenderlich的散点图教程和Eric Skrooch的实时图示例。 (I hope I spelt those names right!). (我希望我把那些名字拼写正确!)。 Personally, I believe that this is a bug in core-plot and not a coding error though. 我个人认为,这是核心图中的错误,而不是编码错误。 Any help on how to get rid of these troubling lines is greatly appreciated! 非常感谢您对如何摆脱这些麻烦线的任何帮助! Thanks! 谢谢!

-(void)configurePlots {

    //Get graph and plot space
    CPTGraph *graph = self.hostView.hostedGraph;
    CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *) graph.defaultPlotSpace;

    //Create the four plots
    CPTScatterPlot *channel1Plot = [[CPTScatterPlot alloc]init];
    channel1Plot.dataSource = self;
    channel1Plot.identifier = @"channel1";
    [graph addPlot:channel1Plot toPlotSpace:plotSpace];

    CPTScatterPlot *channel2Plot = [[CPTScatterPlot alloc]init];
    channel2Plot.dataSource = self;
    channel2Plot.identifier = @"channel2";
    [graph addPlot:channel2Plot toPlotSpace:plotSpace];

    CPTScatterPlot *channel3Plot = [[CPTScatterPlot alloc]init];
    channel3Plot.dataSource = self;
    channel3Plot.identifier = @"channel3";
    [graph addPlot:channel3Plot toPlotSpace:plotSpace];

    CPTScatterPlot *channel4Plot = [[CPTScatterPlot alloc]init];
    channel4Plot.dataSource = self;
    channel4Plot.identifier = @"channel4";
    [graph addPlot:channel4Plot toPlotSpace:plotSpace];

    //Set up plot space
    [plotSpace scaleToFitPlots:[NSArray arrayWithObjects:channel1Plot, channel2Plot, channel3Plot, channel4Plot, nil]];
    CPTMutablePlotRange *xRange = [plotSpace.xRange mutableCopy];
    [xRange expandRangeByFactor:CPTDecimalFromCGFloat(1.1f)];
    plotSpace.xRange = xRange;
    CPTMutablePlotRange *yRange = [plotSpace.yRange mutableCopy];
    [yRange expandRangeByFactor:CPTDecimalFromCGFloat(1.2f)];
    plotSpace.yRange = yRange;

    //Create styles and symbols

    CPTMutableLineStyle *channelLineStyle = [channel1Plot.dataLineStyle mutableCopy];
    channelLineStyle.lineWidth = 1.0;
    channelLineStyle.lineColor = [CPTColor redColor];
    channel1Plot.dataLineStyle = channelLineStyle;
    channel2Plot.dataLineStyle = channelLineStyle;
    channel3Plot.dataLineStyle = channelLineStyle;
    channel4Plot.dataLineStyle = channelLineStyle;

}

-(void)configureAxes {

    //Create styles
    CPTMutableTextStyle *axisTitleStyle = [CPTMutableTextStyle textStyle];
    axisTitleStyle.color = [CPTColor whiteColor];
    axisTitleStyle.fontName = @"Helvetica-Bold";
    axisTitleStyle.fontSize = 12.0f;
    CPTMutableLineStyle *axisLineStyle = [CPTMutableLineStyle lineStyle];
    axisLineStyle.lineWidth = 2.0f;
    axisLineStyle.lineColor = [CPTColor whiteColor];
    CPTMutableTextStyle *axisTextStyle = [[CPTMutableTextStyle alloc] init];
    axisTextStyle.color = [CPTColor yellowColor];
    axisTextStyle.fontName = @"Helvetica-Bold";
    axisTextStyle.fontSize = 11.0f;
    CPTMutableLineStyle *tickLineStyle = [CPTMutableLineStyle lineStyle];
    tickLineStyle.lineWidth = 2.0f;
    tickLineStyle.lineColor = [CPTColor yellowColor];
    CPTMutableLineStyle *gridLineStyle = [CPTMutableLineStyle lineStyle];
    tickLineStyle.lineColor = [CPTColor purpleColor];
    tickLineStyle.lineWidth = 1.0f;

    //Get axis set
    CPTXYAxisSet *axisSet = (CPTXYAxisSet *) self.hostView.hostedGraph.axisSet;

    //Configure X-axis
    CPTAxis *x = axisSet.xAxis;
    x.title = @"Time";
    x.titleTextStyle = axisTitleStyle;
    x.titleOffset = 15.0f;
    x.axisLineStyle = axisLineStyle;
    x.labelingPolicy = CPTAxisLabelingPolicyNone;
    x.labelTextStyle = axisTextStyle;
    x.majorTickLineStyle = axisLineStyle;
    x.majorTickLength = 4.0f;
    x.tickDirection = CPTSignNegative;
    CGFloat pointsCount = POINTS_ON_SCREEN;
    NSMutableSet *xLabels = [NSMutableSet setWithCapacity:pointsCount];
    NSMutableSet *xLocations = [NSMutableSet setWithCapacity:pointsCount];
    NSInteger i=0;
    //NSString *loc = [NSString stringWithFormat:@"d",j];
    for (NSInteger j=0; j<POINTS_ON_SCREEN; j++) {
        CPTAxisLabel *label = [[CPTAxisLabel alloc] initWithText:[NSString stringWithFormat:@"%ld", (long)j+1] textStyle:x.labelTextStyle];
        CGFloat location = i++;
        label.tickLocation = CPTDecimalFromCGFloat(location);
        label.offset = x.majorTickLength;
        if (label) {
            [xLabels addObject:label];
            [xLocations addObject:[NSNumber numberWithFloat:location]];
        }
    }
    x.axisLabels = xLabels;
    x.majorTickLocations = xLocations;

    //Configure Y-axis
    axisSet.yAxis.axisConstraints = [CPTConstraints constraintWithLowerOffset:0.0];
    CPTAxis *y = axisSet.yAxis;
    y.title = @"Channel outputs";
    y.titleTextStyle = axisTitleStyle;
    y.titleOffset = -40.0f;
    y.axisLineStyle = axisLineStyle;
    y.majorGridLineStyle = gridLineStyle;
    y.labelingPolicy = CPTAxisLabelingPolicyNone;
    y.labelTextStyle = axisTextStyle;
    y.labelOffset = 16.0f;
    y.majorTickLineStyle = axisLineStyle;
    y.majorTickLength = 4.0f;
    y.minorTickLength = 2.0f;
    y.tickDirection = CPTSignPositive;
    CGFloat majorIncrement = 0.0005;
    CGFloat minorIncrement = 0.0001;
    CGFloat yMax = 0.0040f;
    NSMutableSet *yLabels = [NSMutableSet set];
    NSMutableSet *yMajorLocations = [NSMutableSet set];
    NSMutableSet *yMinorLocations = [NSMutableSet set];
    for (CGFloat j = minorIncrement; j<=yMax; j+=minorIncrement) {
        j = roundf(j*100000)/100000;
        CGFloat mod = j / majorIncrement;
        NSInteger modInt = (NSInteger)mod;
        CGFloat modMantissa = mod - modInt;
        modMantissa = roundf(modMantissa * 100)/100;
        if (modMantissa < 0.1 || modMantissa > 0.9) {
            CPTAxisLabel *label = [[CPTAxisLabel alloc] initWithText:[NSString stringWithFormat:@"%.5f", j] textStyle:y.labelTextStyle];
            NSDecimal location = CPTDecimalFromCGFloat(j);
            //NSNumber *location = [NSNumber numberWithFloat:j];
            label.tickLocation = location;
            label.offset = -y.majorTickLength -y.labelOffset -9;
            //label.offset = 0;
            if (label) {
                [yLabels addObject:label];
            }
            [yMajorLocations addObject:[NSDecimalNumber decimalNumberWithDecimal:location]];
        } else {
            [yMinorLocations addObject:[NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%f",j]]];
        }
    }
    y.axisLabels = yLabels;
    y.majorTickLocations = yMajorLocations;
    y.minorTickLocations = yMinorLocations;
}

-(void)createTimer {
    NSTimer *dataTimer = [NSTimer timerWithTimeInterval:0.002 target:self selector:@selector(dynamicUpdate:) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:dataTimer forMode:NSDefaultRunLoopMode];
}

-(void)dynamicUpdate: (NSTimer *)dataTimer {
    CPTGraph *graph = self.hostView.hostedGraph;
    CPTPlot *channel1Plot = [graph plotWithIdentifier:@"channel1"];
    CPTPlot *channel2Plot = [graph plotWithIdentifier:@"channel2"];
    CPTPlot *channel3Plot = [graph plotWithIdentifier:@"channel3"];
    CPTPlot *channel4Plot = [graph plotWithIdentifier:@"channel4"];
    CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)graph.defaultPlotSpace;


    if (channel1Plot && channel2Plot && channel3Plot && channel4Plot) {
        if ( arrayIndex1 >= POINTS_ON_SCREEN-1
            || arrayIndex2 >= POINTS_ON_SCREEN-1
            || arrayIndex3 >= POINTS_ON_SCREEN-1
            || arrayIndex4 >= POINTS_ON_SCREEN-1) {

            [channel1Array removeObjectAtIndex:0];
            [channel1Plot deleteDataInIndexRange:NSMakeRange(0, 1)];
            [channel2Array removeObjectAtIndex:0];
            [channel2Plot deleteDataInIndexRange:NSMakeRange(0, 1)];
            [channel3Array removeObjectAtIndex:0];
            [channel3Plot deleteDataInIndexRange:NSMakeRange(0, 1)];
            [channel4Array removeObjectAtIndex:0];
            [channel4Plot deleteDataInIndexRange:NSMakeRange(0, 1)];
        }

        plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromUnsignedInteger(currentIndex >= POINTS_ON_SCREEN ? currentIndex-POINTS_ON_SCREEN +1 : 0) length:CPTDecimalFromUnsignedInteger(POINTS_ON_SCREEN-1)];
        currentIndex++;

        [channel1Plot insertDataAtIndex:POINTS_ON_SCREEN-1 numberOfRecords:1];
        [channel2Plot insertDataAtIndex:POINTS_ON_SCREEN-1 numberOfRecords:1];
        [channel3Plot insertDataAtIndex:POINTS_ON_SCREEN-1 numberOfRecords:1];
        [channel4Plot insertDataAtIndex:POINTS_ON_SCREEN-1 numberOfRecords:1];
    }
}


-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot {
    return POINTS_ON_SCREEN;
}

-(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)idx {
   // NSLog(@"Plotting point");
    NSNumber *num = nil;
    if ([self.activityIndicator isAnimating] == YES) {
        [self.activityIndicator stopAnimating];
    }
    switch (fieldEnum) {

        case CPTScatterPlotFieldX:
            if (idx < POINTS_ON_SCREEN) {
                return [NSNumber numberWithUnsignedInteger:idx + currentIndex - POINTS_ON_SCREEN];
            };
            break;

        case CPTScatterPlotFieldY:
            if ([plot.identifier isEqual:@"channel1"]) {
                num = [channel1Array objectAtIndex:idx];
                arrayIndex1 = idx;
            } else if ([plot.identifier isEqual:@"channel2"]) {
                num = [channel2Array objectAtIndex:idx];
                arrayIndex2 = idx;
            } else if ([plot.identifier isEqual:@"channel3"]) {
                num = [channel3Array objectAtIndex:idx];
                arrayIndex3 = idx;
            } else if ([plot.identifier isEqual:@"channel4"]) {
                num = [channel4Array objectAtIndex:idx];
                arrayIndex4 = idx;
            } else {
                NSLog(@"data unavailable");
            }
            break;

        default:
            NSLog(@"ERROR: trying to plot on unidentified axis");
            break;
    }
    NSString *numString = [NSString stringWithFormat:@"%@", num];
    dispatch_async(AddToArrayQ, ^{[self writeData:numString];});
    return num;
}

Unless I have misread this, you are returning a hardcoded value from numberOfRecordsForPlot:, so CorePlot expects to be able to fill all of those points on the x axis. 除非我有误读,否则您从numberOfRecordsForPlot:返回硬编码的值,因此CorePlot希望能够填充x轴上的所有这些点。 Those lines you see are probably due to having initialized those arrays to a constant value (they must be initialized, right, or you would be getting an error when you try to access uninitialized indices?) 您看到的这些行可能是由于已将这些数组初始化为一个常数值(必须将它们初始化,正确,否则当您尝试访问未初始化的索引时会出现错误吗?)

Notice in Eric Skroch's example that he is returning [plotData count] from numberOfRecordsForPlot:, and that plotData is empty on initialization. 请注意,在Eric Skroch的示例中,他正在从numberOfRecordsForPlot:返回[plotData count],并且初始化时plotData为空。

The problem here lied not in hardcoding the numberOfRecordsForPlot: method as RishiG suggested, but in hardcoding the case CPTScatterPlotFieldX in the numberForPlot:field:recordIndex method. 这里的问题不在于如RishiG建议的那样对numberOfRecordsForPlot:方法进行硬编码,而在于对numberForPlot:field:recordIndex方法中的CPTScatterPlotFieldX进行硬编码。 But RishiG's suggestion was a good one nonetheless and I made some changes before I resolved the problem. 但是,RishiG的建议还是不错的,我在解决问题之前做了一些更改。

Original: Arrays channel1Array - channel4Array already contained the data to be plotted and I went on deleting items from the array as I plotted them. 原始数据:数组channel1Array-channel4Array已包含要绘制的数据,我在绘制项目时继续从数组中删除项目。 This meant that I could not use something like [channel1Array count] in the numberOfRecordsForPlot: method and had to rely on the plot index instead. 这意味着我不能在numberOfRecordsForPlot:方法中使用类似[channel1Array count]的方法,而不得不依靠plot索引。 This also would have worked but is inefficient anyway. 这也可以工作,但是效率低下。 Modified: Followed Eric Skroch's example fully to start with empty arrays and update them as plotting proceeded. 修改:完全按照Eric Skroch的示例进行操作,从空数组开始,并随着绘图的进行对其进行更新。

The original case CPTScatterPlotFieldX in the numberForPlot:field:recordIndex method used here had the return value as (idx + currrentIndex - POINTS_ON_SCREEN) which might have produced a negative result. 此处使用的numberForPlot:field:recordIndex方法中的原始案例CPTScatterPlotFieldX的返回值为(idx + currrentIndex-POINTS_ON_SCREEN),可能会产生负面结果。 It looks like core-plot perhaps used its absolute value to get the x-coordinate, and hence the horizontal line. 看起来像核心图可能使用其绝对值来获取x坐标,从而获得水平线。 This can be remedied by removing the if condition and having the return value as (idx + currentIndex - channel1Array.count). 这可以通过删除if条件并将返回值设为(idx + currentIndex-channel1Array.count)来解决。

PS: @vikingosegundo I know, thanks. PS:@vikingosegundo我知道,谢谢。 But I had to take photos in quick succession so my camera's burst mode was best suited! 但是我必须快速连续拍照,所以相机的连拍模式最适合!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM